diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/IDE/Canvas_Context_Property_Pane_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/IDE/Canvas_Context_Property_Pane_spec.js index e909284450..5bcf3d1c01 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/IDE/Canvas_Context_Property_Pane_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/IDE/Canvas_Context_Property_Pane_spec.js @@ -31,7 +31,7 @@ describe("Canvas context Property Pane", function() { cy.focusCodeInput(propertyControlSelector); }, () => { - cy.assertSoftFocusOnPropertyPane(propertyControlSelector); + cy.assertCursorOnCodeInput(propertyControlSelector); }, "Button1", ); @@ -160,7 +160,7 @@ describe("Canvas context Property Pane", function() { cy.focusCodeInput(propertyControlSelector); }, () => { - cy.assertSoftFocusOnPropertyPane(propertyControlSelector); + cy.assertCursorOnCodeInput(propertyControlSelector); }, "Table1", ); @@ -241,7 +241,7 @@ describe("Canvas context Property Pane", function() { cy.focusCodeInput(propertyControlSelector); }, () => { - cy.assertSoftFocusOnPropertyPane(propertyControlSelector); + cy.assertCursorOnCodeInput(propertyControlSelector); }, "Table1", ); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/IDE/MaintainContext&Focus_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/IDE/MaintainContext&Focus_spec.js index f23cd92c6c..c9529e8d7a 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/IDE/MaintainContext&Focus_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/IDE/MaintainContext&Focus_spec.js @@ -1,5 +1,6 @@ import reconnectDatasourceModal from "../../../../locators/ReconnectLocators"; const apiwidget = require("../../../../locators/apiWidgetslocator.json"); +const queryLocators = require("../../../../locators/QueryEditor.json"); import { ObjectsRegistry } from "../../../../support/Objects/Registry"; const homePage = ObjectsRegistry.HomePage; @@ -88,7 +89,7 @@ describe("MaintainContext&Focus", function() { cy.get(`.t--entity-name:contains("Page1")`).click(); cy.get(".t--widget-name").should("have.text", "Text1"); - cy.assertSoftFocusOnPropertyPane(".t--property-control-text", { + cy.assertCursorOnCodeInput(".t--property-control-text", { ch: 2, line: 0, }); @@ -195,4 +196,18 @@ describe("MaintainContext&Focus", function() { // Validate if section with index 1 is expanded dataSources.AssertSectionCollapseState(1, false); }); + + it("10. Maintain focus of form control inputs", () => { + ee.SelectEntityByName("SQL_Query"); + dataSources.ToggleUsePreparedStatement(false); + cy.SearchEntityandOpen("S3_Query"); + cy.get(queryLocators.querySettingsTab).click(); + cy.setQueryTimeout(10000); + + cy.SearchEntityandOpen("SQL_Query"); + cy.get(".t--form-control-SWITCH input").should("be.focused"); + cy.SearchEntityandOpen("S3_Query"); + cy.get(queryLocators.querySettingsTab).click(); + cy.xpath(queryLocators.queryTimeout).should("be.focused"); + }); }); diff --git a/app/client/cypress/support/widgetCommands.js b/app/client/cypress/support/widgetCommands.js index 109306c408..0379a64c11 100644 --- a/app/client/cypress/support/widgetCommands.js +++ b/app/client/cypress/support/widgetCommands.js @@ -617,7 +617,7 @@ Cypress.Commands.add( ); Cypress.Commands.add( - "assertSoftFocusOnPropertyPane", + "assertSoftFocusOnCodeInput", ($selector, cursor = { ch: 0, line: 0 }) => { cy.EnableAllCodeEditors(); cy.get($selector) diff --git a/app/client/src/actions/editorContextActions.ts b/app/client/src/actions/editorContextActions.ts index 8ecbc5e7e3..6574b49c48 100644 --- a/app/client/src/actions/editorContextActions.ts +++ b/app/client/src/actions/editorContextActions.ts @@ -5,9 +5,9 @@ import { PropertyPanelContext, } from "reducers/uiReducers/editorContextReducer"; -export const setFocusableCodeEditorField = (path: string | undefined) => { +export const setFocusableInputField = (path: string | undefined) => { return { - type: ReduxActionTypes.SET_FOCUSABLE_CODE_EDITOR_FIELD, + type: ReduxActionTypes.SET_FOCUSABLE_INPUT_FIELD, payload: { path }, }; }; diff --git a/app/client/src/actions/propertyPaneActions.ts b/app/client/src/actions/propertyPaneActions.ts index 505ffc6070..8f3bec9da9 100644 --- a/app/client/src/actions/propertyPaneActions.ts +++ b/app/client/src/actions/propertyPaneActions.ts @@ -60,13 +60,6 @@ export const setSelectedPropertyTabIndex = (selectedIndex: number) => { }; }; -export const setFocusablePropertyPaneField = (path?: string) => { - return { - type: ReduxActionTypes.SET_FOCUSABLE_PROPERTY_FIELD, - payload: { path }, - }; -}; - export const setSelectedPropertyPanel = ( path: string | undefined, index: number, diff --git a/app/client/src/ce/constants/ReduxActionConstants.tsx b/app/client/src/ce/constants/ReduxActionConstants.tsx index d476f21177..28a9f60c62 100644 --- a/app/client/src/ce/constants/ReduxActionConstants.tsx +++ b/app/client/src/ce/constants/ReduxActionConstants.tsx @@ -687,10 +687,6 @@ export const ReduxActionTypes = { FETCH_CURRENT_TENANT_CONFIG: "FETCH_CURRENT_TENANT_CONFIG", FETCH_CURRENT_TENANT_CONFIG_SUCCESS: "FETCH_CURRENT_TENANT_CONFIG_SUCCESS", SET_FOCUS_HISTORY: "SET_FOCUS_HISTORY", - SET_FOCUSABLE_CODE_EDITOR_FIELD: "SET_FOCUSABLE_CODE_EDITOR_FIELD", - GENERATE_KEY_AND_SET_FOCUSABLE_CODE_EDITOR_FIELD: - "GENERATE_KEY_AND_SET_FOCUSABLE_CODE_EDITOR_FIELD", - SET_FOCUSABLE_PROPERTY_FIELD: "SET_FOCUSABLE_PROPERTY_FIELD", ROUTE_CHANGED: "ROUTE_CHANGED", PAGE_CHANGED: "PAGE_CHANGED", SET_API_PANE_CONFIG_SELECTED_TAB: "SET_API_PANE_CONFIG_SELECTED_TAB", @@ -698,6 +694,7 @@ export const ReduxActionTypes = { SET_API_PANE_RESPONSE_PANE_HEIGHT: "SET_API_PANE_RESPONSE_PANE_HEIGHT", SET_API_RIGHT_PANE_SELECTED_TAB: "SET_API_RIGHT_PANE_SELECTED_TAB", SET_EDITOR_FIELD_FOCUS: "SET_EDITOR_FIELD_FOCUS", + SET_FOCUSABLE_INPUT_FIELD: "SET_FOCUSABLE_INPUT_FIELD", SET_CODE_EDITOR_CURSOR: "SET_CODE_EDITOR_CURSOR", SET_CODE_EDITOR_CURSOR_HISTORY: "SET_CODE_EDITOR_CURSOR_HISTORY", SET_EVAL_POPUP_STATE: "SET_EVAL_POPUP_STATE", diff --git a/app/client/src/components/editorComponents/CodeEditor/index.tsx b/app/client/src/components/editorComponents/CodeEditor/index.tsx index f092fbd118..3ee64784be 100644 --- a/app/client/src/components/editorComponents/CodeEditor/index.tsx +++ b/app/client/src/components/editorComponents/CodeEditor/index.tsx @@ -105,7 +105,7 @@ import { interactionAnalyticsEvent } from "utils/AppsmithUtils"; import { AdditionalDynamicDataTree } from "utils/autocomplete/customTreeTypeDefCreator"; import { getCodeEditorLastCursorPosition, - getIsCodeEditorFocused, + getIsInputFieldFocused, } from "selectors/editorContextSelectors"; import { CodeEditorFocusState, @@ -1153,7 +1153,7 @@ const mapStateToProps = (state: AppState, props: EditorProps) => ({ pluginIdToImageLocation: getPluginIdToImageLocation(state), recentEntities: getRecentEntityIds(state), lintErrors: getEntityLintErrors(state, props.dataTreePath), - editorIsFocused: getIsCodeEditorFocused(state, getEditorIdentifier(props)), + editorIsFocused: getIsInputFieldFocused(state, getEditorIdentifier(props)), editorLastCursorPosition: getCodeEditorLastCursorPosition( state, getEditorIdentifier(props), diff --git a/app/client/src/navigation/FocusElements.ts b/app/client/src/navigation/FocusElements.ts index 6fbd7ca803..9faf375c8e 100644 --- a/app/client/src/navigation/FocusElements.ts +++ b/app/client/src/navigation/FocusElements.ts @@ -19,7 +19,7 @@ import { getCodeEditorHistory, getExplorerSwitchIndex, getPropertyPanelState, - getFocusableCodeEditorField, + getFocusableInputField, getSelectedCanvasDebuggerTab, getWidgetSelectedPropertyTabIndex, } from "selectors/editorContextSelectors"; @@ -31,7 +31,7 @@ import { setPanelPropertiesState, setWidgetSelectedPropertyTabIndex, } from "actions/editorContextActions"; -import { setFocusableCodeEditorField } from "actions/editorContextActions"; +import { setFocusableInputField } from "actions/editorContextActions"; import { getAllDatasourceCollapsibleState, getSelectedWidgets, @@ -75,17 +75,13 @@ import { setPropertyPaneWidthAction, setSelectedPropertyPanels, } from "actions/propertyPaneActions"; -import { - setAllPropertySectionState, - setFocusablePropertyPaneField, -} from "actions/propertyPaneActions"; +import { setAllPropertySectionState } from "actions/propertyPaneActions"; import { setCanvasDebuggerSelectedTab } from "actions/debuggerActions"; import { setAllDatasourceCollapsible, setDatasourceViewMode, } from "actions/datasourceActions"; import { PluginPackageName } from "entities/Action"; -import { getFocusablePropertyPaneField } from "selectors/propertyPaneSelectors"; export enum FocusElement { ApiPaneConfigTabs = "ApiPaneConfigTabs", @@ -105,8 +101,6 @@ export enum FocusElement { JSPaneConfigTabs = "JSPaneConfigTabs", JSPaneResponseTabs = "JSPaneResponseTabs", JSPaneResponseHeight = "JSPaneResponseHeight", - CodeEditor = "CodeEditor", - PropertyField = "PropertyField", PropertySections = "PropertySections", PropertyTabs = "PropertyTabs", PropertyPanelContext = "PropertyPanelContext", @@ -114,6 +108,7 @@ export enum FocusElement { SelectedPropertyPanel = "SelectedPropertyPanel", SelectedWidgets = "SelectedWidgets", SubEntityCollapsibleState = "SubEntityCollapsibleState", + InputField = "InputField", } type Config = { @@ -211,9 +206,9 @@ export const FocusElementsConfig: Record = { ], [FocusEntity.JS_OBJECT]: [ { - name: FocusElement.CodeEditor, - selector: getFocusableCodeEditorField, - setter: setFocusableCodeEditorField, + name: FocusElement.InputField, + selector: getFocusableInputField, + setter: setFocusableInputField, }, { name: FocusElement.JSPaneConfigTabs, @@ -236,9 +231,9 @@ export const FocusElementsConfig: Record = { ], [FocusEntity.QUERY]: [ { - name: FocusElement.CodeEditor, - selector: getFocusableCodeEditorField, - setter: setFocusableCodeEditorField, + name: FocusElement.InputField, + selector: getFocusableInputField, + setter: setFocusableInputField, }, { name: FocusElement.QueryPaneConfigTabs, @@ -267,18 +262,13 @@ export const FocusElementsConfig: Record = { defaultValue: 0, }, { - name: FocusElement.PropertyField, - selector: getFocusablePropertyPaneField, - setter: setFocusablePropertyPaneField, + name: FocusElement.InputField, + selector: getFocusableInputField, + setter: setFocusableInputField, defaultValue: "", }, ], [FocusEntity.API]: [ - { - name: FocusElement.CodeEditor, - selector: getFocusableCodeEditorField, - setter: setFocusableCodeEditorField, - }, { name: FocusElement.ApiPaneConfigTabs, selector: getApiPaneConfigSelectedTabIndex, @@ -302,6 +292,11 @@ export const FocusElementsConfig: Record = { setter: setApiPaneResponsePaneHeight, defaultValue: ActionExecutionResizerHeight, }, + { + name: FocusElement.InputField, + selector: getFocusableInputField, + setter: setFocusableInputField, + }, { name: FocusElement.ApiRightPaneTabs, selector: getApiRightPaneSelectedTab, diff --git a/app/client/src/pages/Editor/APIEditor/Pagination.tsx b/app/client/src/pages/Editor/APIEditor/Pagination.tsx index 77107688bd..a67a389a66 100644 --- a/app/client/src/pages/Editor/APIEditor/Pagination.tsx +++ b/app/client/src/pages/Editor/APIEditor/Pagination.tsx @@ -17,6 +17,7 @@ import lightmodeThumbnail from "assets/icons/gifs/lightmode_thumbnail.png"; import darkmodeThumbnail from "assets/icons/gifs/darkmode_thumbnail.png"; interface PaginationProps { + actionName: string; onTestClick: (test?: "PREV" | "NEXT") => void; paginationType: PaginationType; theme?: EditorTheme; @@ -183,6 +184,7 @@ export default function Pagination(props: PaginationProps) { border={CodeEditorBorder.ALL_SIDE} className="t--apiFormPaginationPrev" fill={!!true} + focusElementName={`${props.actionName}.actionConfiguration.prev`} height="100%" name="actionConfiguration.prev" theme={props.theme} @@ -208,6 +210,7 @@ export default function Pagination(props: PaginationProps) { border={CodeEditorBorder.ALL_SIDE} className="t--apiFormPaginationNext" fill={!!true} + focusElementName={`${props.actionName}.actionConfiguration.next`} height="100%" name="actionConfiguration.next" theme={props.theme} diff --git a/app/client/src/pages/Editor/APIEditor/RapidApiEditorForm.tsx b/app/client/src/pages/Editor/APIEditor/RapidApiEditorForm.tsx index 3281ecda49..22a7ef956b 100644 --- a/app/client/src/pages/Editor/APIEditor/RapidApiEditorForm.tsx +++ b/app/client/src/pages/Editor/APIEditor/RapidApiEditorForm.tsx @@ -256,6 +256,7 @@ function RapidApiEditorForm(props: Props) { title: "Pagination", panelComponent: ( diff --git a/app/client/src/pages/Editor/APIEditor/RestAPIForm.tsx b/app/client/src/pages/Editor/APIEditor/RestAPIForm.tsx index 1ea013c29b..5c2dd092ae 100644 --- a/app/client/src/pages/Editor/APIEditor/RestAPIForm.tsx +++ b/app/client/src/pages/Editor/APIEditor/RestAPIForm.tsx @@ -63,6 +63,7 @@ function ApiEditorForm(props: Props) { formName={API_EDITOR_FORM_NAME} paginationUIComponent={ (null); + const dispatch = useDispatch(); + const entityInfo = identifyEntityFromPath( + window.location.pathname, + window.location.hash, + ); + const handleOnFocus = () => { + if (props.config.configProperty) { + // Need an additional identifier to trigger another render when configProperty + // are same for two different entitites + dispatch( + setFocusableInputField( + `${entityInfo.id}.${props.config.configProperty}`, + ), + ); + } + }; + + const shouldFocusPropertyPath: boolean = useSelector((state: AppState) => + getIsInputFieldFocused( + state, + `${entityInfo.id}.${props.config.configProperty}`, + ), + ); + + useEffect(() => { + if (shouldFocusPropertyPath) { + setTimeout(() => { + if (shouldFocusOnPropertyControl(controlRef.current)) { + const focusableElement = getPropertyControlFocusElement( + controlRef.current, + ); + focusableElement?.scrollIntoView({ + block: "center", + behavior: "smooth", + }); + focusableElement?.focus(); + } + }, 0); + } + }, [shouldFocusPropertyPath]); if (props.multipleConfig?.length) { top = (
@@ -86,7 +136,11 @@ export default function FormConfig(props: FormConfigProps) { return (
- + {props.config.controlType === "CHECKBOX" ? ( <> {props.children} diff --git a/app/client/src/pages/Editor/PropertyPane/PropertyControl.tsx b/app/client/src/pages/Editor/PropertyPane/PropertyControl.tsx index e4fb60973a..a3bc9574c4 100644 --- a/app/client/src/pages/Editor/PropertyPane/PropertyControl.tsx +++ b/app/client/src/pages/Editor/PropertyPane/PropertyControl.tsx @@ -34,7 +34,6 @@ import { THEME_BINDING_REGEX, } from "utils/DynamicBindingUtils"; import { - getShouldFocusPropertyPath, getWidgetPropsForPropertyName, WidgetProperties, } from "selectors/propertyPaneSelectors"; @@ -55,8 +54,9 @@ import { shouldFocusOnPropertyControl, } from "utils/editorContextUtils"; import PropertyPaneHelperText from "./PropertyPaneHelperText"; -import { setFocusablePropertyPaneField } from "actions/propertyPaneActions"; import WidgetFactory from "utils/WidgetFactory"; +import { getIsInputFieldFocused } from "selectors/editorContextSelectors"; +import { setFocusableInputField } from "actions/editorContextActions"; type Props = PropertyPaneControlConfig & { panel: IPanelProps; @@ -91,7 +91,7 @@ const PropertyControl = memo((props: Props) => { const hasDispatchedPropertyFocus = useRef(false); const shouldFocusPropertyPath: boolean = useSelector( (state: AppState) => - getShouldFocusPropertyPath( + getIsInputFieldFocused( state, dataTreePath, hasDispatchedPropertyFocus.current, @@ -576,7 +576,7 @@ const PropertyControl = memo((props: Props) => { if (!shouldFocusPropertyPath) { hasDispatchedPropertyFocus.current = true; setTimeout(() => { - dispatch(setFocusablePropertyPaneField(dataTreePath)); + dispatch(setFocusableInputField(dataTreePath)); }, 0); } }; diff --git a/app/client/src/reducers/uiReducers/editorContextReducer.ts b/app/client/src/reducers/uiReducers/editorContextReducer.ts index 24be20ae54..ea70c46b88 100644 --- a/app/client/src/reducers/uiReducers/editorContextReducer.ts +++ b/app/client/src/reducers/uiReducers/editorContextReducer.ts @@ -32,7 +32,7 @@ export type EditorContextState = { entityCollapsibleFields: Record; subEntityCollapsibleFields: Record; explorerSwitchIndex: number; - focusableCodeEditor?: string; + focusedInputField?: string; codeEditorHistory: Record; propertySectionState: Record; selectedPropertyTabIndex: number; @@ -61,14 +61,14 @@ export const isSubEntities = (name: string): boolean => { * Context Reducer to store states of different components of editor */ export const editorContextReducer = createImmerReducer(initialState, { - [ReduxActionTypes.SET_FOCUSABLE_CODE_EDITOR_FIELD]: ( + [ReduxActionTypes.SET_FOCUSABLE_INPUT_FIELD]: ( state: EditorContextState, action: { payload: { path: string }; }, ) => { const { path } = action.payload; - state.focusableCodeEditor = path; + state.focusedInputField = path; }, [ReduxActionTypes.SET_CODE_EDITOR_CURSOR]: ( state: EditorContextState, diff --git a/app/client/src/reducers/uiReducers/propertyPaneReducer.tsx b/app/client/src/reducers/uiReducers/propertyPaneReducer.tsx index 42a7467f25..92678d197e 100644 --- a/app/client/src/reducers/uiReducers/propertyPaneReducer.tsx +++ b/app/client/src/reducers/uiReducers/propertyPaneReducer.tsx @@ -76,12 +76,6 @@ const propertyPaneReducer = createImmerReducer(initialState, { ) => { state.width = action.payload; }, - [ReduxActionTypes.SET_FOCUSABLE_PROPERTY_FIELD]: ( - state: PropertyPaneReduxState, - action: ReduxAction<{ path: string }>, - ) => { - state.focusedProperty = action.payload.path; - }, [ReduxActionTypes.SET_SELECTED_PANEL_PROPERTY]: ( state: PropertyPaneReduxState, action: { diff --git a/app/client/src/sagas/editorContextSagas.ts b/app/client/src/sagas/editorContextSagas.ts index 22f552f683..aba7cdd41d 100644 --- a/app/client/src/sagas/editorContextSagas.ts +++ b/app/client/src/sagas/editorContextSagas.ts @@ -13,7 +13,7 @@ import { all, put, takeLatest } from "redux-saga/effects"; import { CodeEditorFocusState, setCodeEditorCursorAction, - setFocusableCodeEditorField, + setFocusableInputField, } from "actions/editorContextActions"; import { FocusEntity, identifyEntityFromPath } from "navigation/FocusEntity"; @@ -28,10 +28,11 @@ function* setEditorFieldFocus(action: ReduxAction) { window.location.pathname, window.location.hash, ); + const ignoredEntities = [FocusEntity.DATASOURCE]; if (key) { - if (entityInfo.entity !== FocusEntity.PROPERTY_PANE) { - yield put(setFocusableCodeEditorField(key)); + if (!ignoredEntities.includes(entityInfo.entity)) { + yield put(setFocusableInputField(key)); } yield put(setCodeEditorCursorAction(key, cursorPosition)); } diff --git a/app/client/src/selectors/editorContextSelectors.ts b/app/client/src/selectors/editorContextSelectors.ts index 9c9a09c10f..84f14a6527 100644 --- a/app/client/src/selectors/editorContextSelectors.ts +++ b/app/client/src/selectors/editorContextSelectors.ts @@ -11,8 +11,8 @@ import { createSelector } from "reselect"; import { selectFeatureFlags } from "selectors/usersSelectors"; import FeatureFlags from "entities/FeatureFlags"; -export const getFocusableCodeEditorField = (state: AppState) => - state.ui.editorContext.focusableCodeEditor; +export const getFocusableInputField = (state: AppState) => + state.ui.editorContext.focusedInputField; export const getCodeEditorHistory = (state: AppState) => state.ui.editorContext.codeEditorHistory; @@ -67,9 +67,9 @@ export const getSelectedPropertyTabIndex = createSelector( }, ); -export const getIsCodeEditorFocused = createSelector( +export const getIsInputFieldFocused = createSelector( [ - getFocusableCodeEditorField, + getFocusableInputField, selectFeatureFlags, (_state: AppState, key: string | undefined) => key, ], diff --git a/app/client/src/selectors/propertyPaneSelectors.tsx b/app/client/src/selectors/propertyPaneSelectors.tsx index 3863129d22..27c2df5456 100644 --- a/app/client/src/selectors/propertyPaneSelectors.tsx +++ b/app/client/src/selectors/propertyPaneSelectors.tsx @@ -20,6 +20,7 @@ import { EVALUATION_PATH } from "utils/DynamicBindingUtils"; import { generateClassName } from "utils/generators"; import { getWidgets } from "sagas/selectors"; import { RegisteredWidgetFeatures } from "utils/WidgetFeatures"; +import { getFocusableInputField } from "./editorContextSelectors"; export type WidgetProperties = WidgetProps & { [EVALUATION_PATH]?: DataTreeEntity; @@ -266,18 +267,6 @@ export const getIsPropertyPaneVisible = createSelector( export const getPropertyPaneWidth = (state: AppState) => { return state.ui.propertyPane.width; }; -export const getFocusablePropertyPaneField = (state: AppState) => - state.ui.propertyPane.focusedProperty; - -export const getShouldFocusPropertyPath = createSelector( - [ - getFocusablePropertyPaneField, - (_state: AppState, key: string | undefined) => key, - ], - (focusableField: string | undefined, key: string | undefined): boolean => { - return !!(key && focusableField === key); - }, -); export const getSelectedPropertyPanelIndex = createSelector( [ @@ -296,7 +285,7 @@ export const getSelectedPropertyPanelIndex = createSelector( export const getShouldFocusPropertySearch = createSelector( getIsCurrentWidgetRecentlyAdded, - getFocusablePropertyPaneField, + getFocusableInputField, ( isCurrentWidgetRecentlyAdded: boolean, focusableField: string | undefined,