aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuillaume Peoc'h <guillaume.peoch@sonarsource.com>2022-05-11 14:28:30 +0200
committersonartech <sonartech@sonarsource.com>2022-05-13 20:02:50 +0000
commitb49e9d58f17abfa9d2824a0a0347cb61ff9b014a (patch)
tree93117c1c42632e74e9e2ec047ba198950686baed
parentd782e04fa04567dcfafd237bc4f10a6c62f13972 (diff)
downloadsonarqube-b49e9d58f17abfa9d2824a0a0347cb61ff9b014a.tar.gz
sonarqube-b49e9d58f17abfa9d2824a0a0347cb61ff9b014a.zip
SONAR-16340 Replace keymaster in Search
-rw-r--r--server/sonar-web/src/main/js/app/components/search/Search.tsx51
-rw-r--r--server/sonar-web/src/main/js/app/components/search/__tests__/Search-test.tsx23
-rw-r--r--server/sonar-web/src/main/js/helpers/keycodes.ts8
-rw-r--r--server/sonar-web/src/main/js/helpers/testUtils.ts9
4 files changed, 59 insertions, 32 deletions
diff --git a/server/sonar-web/src/main/js/app/components/search/Search.tsx b/server/sonar-web/src/main/js/app/components/search/Search.tsx
index 4ddad49b88e..47549c7d890 100644
--- a/server/sonar-web/src/main/js/app/components/search/Search.tsx
+++ b/server/sonar-web/src/main/js/app/components/search/Search.tsx
@@ -17,7 +17,6 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import key from 'keymaster';
import { debounce, keyBy, uniqBy } from 'lodash';
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
@@ -29,7 +28,7 @@ import SearchBox from '../../../components/controls/SearchBox';
import ClockIcon from '../../../components/icons/ClockIcon';
import { lazyLoadComponent } from '../../../components/lazyLoadComponent';
import DeferredSpinner from '../../../components/ui/DeferredSpinner';
-import { KeyboardCodes } from '../../../helpers/keycodes';
+import { KeyboardKeys } from '../../../helpers/keycodes';
import { translate, translateWithParameters } from '../../../helpers/l10n';
import { scrollToElement } from '../../../helpers/scrolling';
import { getComponentOverviewUrl } from '../../../helpers/urls';
@@ -77,11 +76,7 @@ export class Search extends React.PureComponent<WithRouterProps, State> {
componentDidMount() {
this.mounted = true;
- key('s', () => {
- this.focusInput();
- this.openSearch();
- return false;
- });
+ document.addEventListener('keydown', this.handleSKeyDown);
}
componentDidUpdate(_prevProps: WithRouterProps, prevState: State) {
@@ -92,7 +87,7 @@ export class Search extends React.PureComponent<WithRouterProps, State> {
componentWillUnmount() {
this.mounted = false;
- key.unbind('s');
+ document.removeEventListener('keydown', this.handleSKeyDown);
}
focusInput = () => {
@@ -227,9 +222,8 @@ export class Search extends React.PureComponent<WithRouterProps, State> {
const list = this.getPlainComponentsList(results, more);
const index = list.indexOf(selected);
return index > 0 ? { selected: list[index - 1] } : null;
- } else {
- return null;
}
+ return null;
});
};
@@ -239,9 +233,8 @@ export class Search extends React.PureComponent<WithRouterProps, State> {
const list = this.getPlainComponentsList(results, more);
const index = list.indexOf(selected);
return index >= 0 && index < list.length - 1 ? { selected: list[index + 1] } : null;
- } else {
- return null;
}
+ return null;
});
};
@@ -278,22 +271,38 @@ export class Search extends React.PureComponent<WithRouterProps, State> {
}
};
+ handleSKeyDown = (event: KeyboardEvent) => {
+ const { tagName } = event.target as HTMLElement;
+ const isInput = tagName === 'INPUT' || tagName === 'SELECT' || tagName === 'TEXTAREA';
+ if (event.key === KeyboardKeys.KeyS && !isInput) {
+ event.preventDefault();
+ this.focusInput();
+ this.openSearch();
+ }
+ };
+
handleKeyDown = (event: React.KeyboardEvent) => {
- switch (event.nativeEvent.code) {
- case KeyboardCodes.Enter:
+ switch (event.nativeEvent.key) {
+ case KeyboardKeys.Enter:
event.preventDefault();
+ event.nativeEvent.stopImmediatePropagation();
this.openSelected();
- return;
- case KeyboardCodes.UpArrow:
+ break;
+ case KeyboardKeys.UpArrow:
event.preventDefault();
+ event.nativeEvent.stopImmediatePropagation();
this.selectPrevious();
- return;
- case KeyboardCodes.DownArrow:
+ break;
+ case KeyboardKeys.Escape:
+ event.preventDefault();
+ event.nativeEvent.stopImmediatePropagation();
+ this.closeSearch();
+ break;
+ case KeyboardKeys.DownArrow:
event.preventDefault();
+ event.nativeEvent.stopImmediatePropagation();
this.selectNext();
- // keep this return to prevent fall-through in case more cases will be adder later
- // eslint-disable-next-line no-useless-return
- return;
+ break;
}
};
diff --git a/server/sonar-web/src/main/js/app/components/search/__tests__/Search-test.tsx b/server/sonar-web/src/main/js/app/components/search/__tests__/Search-test.tsx
index 77f6e7b2fbd..39d4e1d3895 100644
--- a/server/sonar-web/src/main/js/app/components/search/__tests__/Search-test.tsx
+++ b/server/sonar-web/src/main/js/app/components/search/__tests__/Search-test.tsx
@@ -19,9 +19,9 @@
*/
import { shallow, ShallowWrapper } from 'enzyme';
import * as React from 'react';
-import { KeyboardCodes } from '../../../../helpers/keycodes';
+import { KeyboardKeys } from '../../../../helpers/keycodes';
import { mockRouter } from '../../../../helpers/testMocks';
-import { elementKeydown } from '../../../../helpers/testUtils';
+import { elementKeydown, keydown } from '../../../../helpers/testUtils';
import { ComponentQualifier } from '../../../../types/component';
import { Search } from '../Search';
@@ -57,7 +57,7 @@ it('opens selected project on enter', () => {
selected: selectedKey
});
- elementKeydown(form.find('SearchBox'), KeyboardCodes.Enter);
+ elementKeydown(form.find('SearchBox'), KeyboardKeys.Enter);
expect(router.push).toBeCalledWith({ pathname: '/dashboard', query: { id: selectedKey } });
});
@@ -73,7 +73,7 @@ it('opens selected portfolio on enter', () => {
selected: selectedKey
});
- elementKeydown(form.find('SearchBox'), KeyboardCodes.Enter);
+ elementKeydown(form.find('SearchBox'), KeyboardKeys.Enter);
expect(router.push).toBeCalledWith({ pathname: '/portfolio', query: { id: selectedKey } });
});
@@ -89,7 +89,7 @@ it('opens selected subportfolio on enter', () => {
selected: selectedKey
});
- elementKeydown(form.find('SearchBox'), KeyboardCodes.Enter);
+ elementKeydown(form.find('SearchBox'), KeyboardKeys.Enter);
expect(router.push).toBeCalledWith({ pathname: '/portfolio', query: { id: selectedKey } });
});
@@ -101,6 +101,15 @@ it('shows warning about short input', () => {
expect(form.find('.navbar-search-input-hint')).toMatchSnapshot();
});
+it('should open the results when pressing key S and close it when pressing Escape', () => {
+ const router = mockRouter();
+ const form = shallowRender({ router });
+ keydown({ key: KeyboardKeys.KeyS });
+ expect(form.state().open).toBe(true);
+ elementKeydown(form.find('SearchBox'), KeyboardKeys.Escape);
+ expect(form.state().open).toBe(false);
+});
+
function shallowRender(props: Partial<Search['props']> = {}) {
return shallow<Search>(
// @ts-ignore
@@ -113,12 +122,12 @@ function component(key: string, qualifier = ComponentQualifier.Project) {
}
function next(form: ShallowWrapper<Search['props'], Search['state']>, expected: string) {
- elementKeydown(form.find('SearchBox'), KeyboardCodes.DownArrow);
+ elementKeydown(form.find('SearchBox'), KeyboardKeys.DownArrow);
expect(form.state().selected).toBe(expected);
}
function prev(form: ShallowWrapper<Search['props'], Search['state']>, expected: string) {
- elementKeydown(form.find('SearchBox'), KeyboardCodes.UpArrow);
+ elementKeydown(form.find('SearchBox'), KeyboardKeys.UpArrow);
expect(form.state().selected).toBe(expected);
}
diff --git a/server/sonar-web/src/main/js/helpers/keycodes.ts b/server/sonar-web/src/main/js/helpers/keycodes.ts
index eb3229aebd2..04c3b908fac 100644
--- a/server/sonar-web/src/main/js/helpers/keycodes.ts
+++ b/server/sonar-web/src/main/js/helpers/keycodes.ts
@@ -37,6 +37,11 @@ export enum KeyboardCodes {
}
export enum KeyboardKeys {
+ Escape = 'Escape',
+ UpArrow = 'ArrowUp',
+ DownArrow = 'ArrowDown',
+ Enter = 'Enter',
+ Space = ' ',
Alt = 'Alt',
KeyF = 'f',
KeyA = 'a',
@@ -44,6 +49,5 @@ export enum KeyboardKeys {
KeyI = 'i',
KeyC = 'c',
KeyT = 't',
- Space = ' ',
- Escape = 'Escape'
+ KeyS = 's'
}
diff --git a/server/sonar-web/src/main/js/helpers/testUtils.ts b/server/sonar-web/src/main/js/helpers/testUtils.ts
index e43030383a4..1b1abba7dbe 100644
--- a/server/sonar-web/src/main/js/helpers/testUtils.ts
+++ b/server/sonar-web/src/main/js/helpers/testUtils.ts
@@ -87,10 +87,15 @@ export function keydown(args: { code?: KeyboardCodes; key?: KeyboardKeys }): voi
document.dispatchEvent(event);
}
-export function elementKeydown(element: ShallowWrapper, code: KeyboardCodes): void {
+export function elementKeydown(element: ShallowWrapper, key: KeyboardKeys): void {
const event = {
currentTarget: { element },
- nativeEvent: { code },
+ nativeEvent: {
+ key,
+ stopImmediatePropagation: () => {
+ /* noop */
+ }
+ },
preventDefault() {
/*noop*/
}