Commit be8c6203 by Marcus Andersson Committed by GitHub

Select: fixes so component loses focus on selecting value or pressing outside of input. (#24008)

* changed the value container to a class component to get it to work with focus (maybe something with context?).

* added e2e tests to verify that the select focus is working as it should.

* fixed according to feedback.

* updated snapshot.
parent a35babee
import { e2e } from '@grafana/e2e';
e2e.scenario({
describeName: 'Select focus/unfocus tests',
itName: 'Tests select focus/unfocus scenarios',
addScenarioDataSource: false,
addScenarioDashBoard: false,
skipScenario: false,
scenario: () => {
e2e.flows.openDashboard('5SdHCadmz');
e2e.pages.Dashboard.Toolbar.toolbarItems('Dashboard settings').click();
e2e.components.FolderPicker.container()
.should('be.visible')
.within(() => {
e2e.components.Select.input()
.should('be.visible')
.click();
e2e.components.Select.option()
.should('be.visible')
.first()
.click();
e2e.components.Select.input()
.should('be.visible')
.should('have.focus');
});
e2e.pages.Dashboard.Settings.General.title().click();
e2e.components.FolderPicker.container()
.should('be.visible')
.within(() => {
e2e.components.Select.input()
.should('be.visible')
.should('not.have.focus');
});
},
});
......@@ -98,6 +98,7 @@ export const Components = {
},
Select: {
option: 'Select option',
input: () => 'input[id*="react-select-"]',
},
FieldConfigEditor: {
content: 'Field config editor content',
......@@ -105,4 +106,7 @@ export const Components = {
OverridesConfigEditor: {
content: 'Field overrides editor content',
},
FolderPicker: {
container: 'Folder picker select container',
},
};
......@@ -49,6 +49,7 @@ export const Pages = {
sectionItems: (item: string) => `Dashboard settings section item ${item}`,
saveDashBoard: 'Dashboard settings aside actions Save button',
saveAsDashBoard: 'Dashboard settings aside actions Save As button',
title: 'Dashboard settings page title',
},
Variables: {
List: {
......
......@@ -231,22 +231,7 @@ export function SelectBase<T>({
components={{
MenuList: SelectMenu,
Group: SelectOptionGroup,
ValueContainer: (props: any) => {
const { menuIsOpen } = props.selectProps;
if (
Array.isArray(props.children) &&
Array.isArray(props.children[0]) &&
maxVisibleValues !== undefined &&
!(showAllSelectedWhenOpen && menuIsOpen)
) {
const [valueChildren, ...otherChildren] = props.children;
const truncatedValues = valueChildren.slice(0, maxVisibleValues);
return <ValueContainer {...props} children={[truncatedValues, ...otherChildren]} />;
}
return <ValueContainer {...props} />;
},
ValueContainer,
Placeholder: (props: any) => (
<div
{...props.innerProps}
......
import React from 'react';
import React, { ReactNode } from 'react';
import { cx } from 'emotion';
import { useTheme } from '../../themes/ThemeContext';
import { GrafanaTheme } from '@grafana/data';
import { withTheme } from '../../themes/ThemeContext';
import { getSelectStyles } from './getSelectStyles';
export const ValueContainer = (props: any) => {
const theme = useTheme();
class UnthemedValueContainer extends React.Component<any & { theme: GrafanaTheme }> {
render() {
const { children } = this.props;
const { selectProps } = this.props;
if (
selectProps &&
Array.isArray(children) &&
Array.isArray(children[0]) &&
selectProps.maxVisibleValues !== undefined &&
!(selectProps.showAllSelectedWhenOpen && selectProps.menuIsOpen)
) {
const [valueChildren, ...otherChildren] = children;
const truncatedValues = valueChildren.slice(0, selectProps.maxVisibleValues);
return this.renderContainer([truncatedValues, ...otherChildren]);
}
return this.renderContainer(children);
}
renderContainer(children?: ReactNode) {
const { isMulti, theme } = this.props;
const styles = getSelectStyles(theme);
const { children, isMulti } = props;
return <div className={cx(styles.valueContainer, isMulti && styles.valueContainerMulti)}>{children}</div>;
};
const className = cx(styles.valueContainer, isMulti && styles.valueContainerMulti);
return <div className={className}>{children}</div>;
}
}
export const ValueContainer = withTheme(UnthemedValueContainer);
import React, { PureComponent } from 'react';
import { debounce } from 'lodash';
import { AsyncSelect } from '@grafana/ui';
import { AppEvents, SelectableValue } from '@grafana/data';
import { debounce } from 'lodash';
import appEvents from '../../app_events';
import { getBackendSrv } from '@grafana/runtime';
import { selectors } from '@grafana/e2e-selectors';
import appEvents from '../../app_events';
import { contextSrv } from 'app/core/services/context_srv';
import { DashboardSearchHit } from 'app/features/search/types';
......@@ -148,7 +149,7 @@ export class FolderPicker extends PureComponent<Props, State> {
const { enableCreateNew, useNewForms } = this.props;
return (
<>
<div aria-label={selectors.components.FolderPicker.container}>
{useNewForms && (
<AsyncSelect
loadingMessage="Loading folders..."
......@@ -180,7 +181,7 @@ export class FolderPicker extends PureComponent<Props, State> {
</div>
</div>
)}
</>
</div>
);
}
}
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`FolderPicker should render 1`] = `
<Fragment>
<div
aria-label="Folder picker select container"
>
<div
className="gf-form-inline"
>
......@@ -26,5 +28,5 @@ exports[`FolderPicker should render 1`] = `
/>
</div>
</div>
</Fragment>
</div>
`;
......@@ -19,7 +19,7 @@
</aside>
<div class="dashboard-settings__content" ng-if="ctrl.viewId === 'settings'">
<h3 class="dashboard-settings__header">
<h3 class="dashboard-settings__header" aria-label="{{::ctrl.selectors.title}}">
General
</h3>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment