diff --git a/static/app/components/contextPickerModal.spec.tsx b/static/app/components/contextPickerModal.spec.tsx index 06bc498defb7b0..88dce5a215711f 100644 --- a/static/app/components/contextPickerModal.spec.tsx +++ b/static/app/components/contextPickerModal.spec.tsx @@ -65,6 +65,7 @@ describe('ContextPickerModal', function () { render(getComponent()); expect(screen.getByText('Select an Organization')).toBeInTheDocument(); + expect(screen.getByRole('textbox')).toHaveFocus(); expect(screen.queryByText('Select a Project to continue')).not.toBeInTheDocument(); }); @@ -144,6 +145,7 @@ describe('ContextPickerModal', function () { // Should see 1 selected, and 1 as an option expect(screen.getAllByText('org-slug')).toHaveLength(2); + expect(screen.getByRole('textbox')).toHaveFocus(); expect(await screen.findByText('My Projects')).toBeInTheDocument(); expect(screen.getByText(project.slug)).toBeInTheDocument(); expect(screen.getByText(project2.slug)).toBeInTheDocument(); @@ -180,6 +182,7 @@ describe('ContextPickerModal', function () { // Should not have anything selected expect(screen.getByText('Select an Organization')).toBeInTheDocument(); + expect(screen.getByRole('textbox')).toHaveFocus(); // Select org2 await selectEvent.select(screen.getByText('Select an Organization'), org2.slug); diff --git a/static/app/components/contextPickerModal.tsx b/static/app/components/contextPickerModal.tsx index 4e5cf53c3f6b53..7955357540a144 100644 --- a/static/app/components/contextPickerModal.tsx +++ b/static/app/components/contextPickerModal.tsx @@ -1,5 +1,4 @@ import {Component, Fragment} from 'react'; -import {findDOMNode} from 'react-dom'; import {components} from 'react-select'; import styled from '@emotion/styled'; import type {Query} from 'history'; @@ -69,6 +68,10 @@ type Props = ModalRenderProps & { allowAllProjectsSelection?: boolean; }; +function autoFocusReactSelect(reactSelectRef: any) { + reactSelectRef?.select?.focus?.(); +} + const selectStyles: StylesConfig = { menu: provided => ({ ...provided, @@ -116,12 +119,6 @@ class ContextPickerModal extends Component { onFinishTimeout: number | undefined = undefined; - // TODO(ts) The various generics in react-select types make getting this - // right hard. - orgSelect: any | null = null; - projectSelect: any | null = null; - configSelect: any | null = null; - // Performs checks to see if we need to prompt user // i.e. When there is only 1 org and no project is needed or // there is only 1 org and only 1 project (which should be rare) @@ -178,21 +175,6 @@ class ContextPickerModal extends Component { ) ?? undefined; }; - doFocus = (ref: any | null) => { - if (!ref || this.props.loading) { - return; - } - - // eslint-disable-next-line react/no-find-dom-node - const el = findDOMNode(ref) as HTMLElement; - - if (el !== null) { - const input = el.querySelector('input'); - - input?.focus(); - } - }; - handleSelectOrganization = ({value}: {value: string}) => { // If we do not need to select a project, we can early return after selecting an org // No need to fetch org details @@ -316,10 +298,7 @@ class ContextPickerModal extends Component { return ( { - this.projectSelect = ref; - this.doFocus(this.projectSelect); - }} + ref={autoFocusReactSelect} placeholder={t('Select a Project to continue')} name="project" options={projectOptions} @@ -354,10 +333,7 @@ class ContextPickerModal extends Component { ]; return ( { - this.configSelect = ref; - this.doFocus(this.configSelect); - }} + ref={autoFocusReactSelect} placeholder={t('Select a configuration to continue')} name="configurations" options={options} @@ -398,18 +374,14 @@ class ContextPickerModal extends Component { return ( -
{this.headerText}
+
+
{this.headerText}
+
{loading && } {needOrg && ( { - this.orgSelect = ref; - if (shouldShowProjectSelector) { - return; - } - this.doFocus(this.orgSelect); - }} + ref={shouldShowProjectSelector ? undefined : autoFocusReactSelect} placeholder={t('Select an Organization')} name="organization" options={orgChoices}