DataSourcePicker: fix flickering datasource dropdown (#67206)

* fix flickering

* refactor onClose/onOpen

* do not set value of input, make the placeholder look like the value instead

* Show search icon when the dropdown is open

---------

Co-authored-by: Ivan Ortega <ivanortegaalba@gmail.com>
This commit is contained in:
Oscar Kilhed 2023-04-26 09:08:32 +02:00 committed by GitHub
parent 926abcf6aa
commit 044d7f61c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -1,6 +1,5 @@
import { css } from '@emotion/css'; import { css } from '@emotion/css';
import { useDialog } from '@react-aria/dialog'; import { useDialog } from '@react-aria/dialog';
import { FocusScope } from '@react-aria/focus';
import { useOverlay } from '@react-aria/overlays'; import { useOverlay } from '@react-aria/overlays';
import React, { useCallback, useRef, useState } from 'react'; import React, { useCallback, useRef, useState } from 'react';
import { usePopper } from 'react-popper'; import { usePopper } from 'react-popper';
@ -37,6 +36,7 @@ export function DataSourceDropdown(props: DataSourceDropdownProps) {
const openDropdown = () => { const openDropdown = () => {
reportInteraction(INTERACTION_EVENT_NAME, { item: INTERACTION_ITEM.OPEN_DROPDOWN }); reportInteraction(INTERACTION_EVENT_NAME, { item: INTERACTION_ITEM.OPEN_DROPDOWN });
setOpen(true); setOpen(true);
markerElement?.focus();
}; };
const currentDataSourceInstanceSettings = useDatasource(current); const currentDataSourceInstanceSettings = useDatasource(current);
@ -45,13 +45,15 @@ export function DataSourceDropdown(props: DataSourceDropdownProps) {
placement: 'bottom-start', placement: 'bottom-start',
}); });
const onClose = useCallback(() => {
setOpen(false);
markerElement?.blur();
}, [setOpen, markerElement]);
const ref = useRef<HTMLDivElement>(null); const ref = useRef<HTMLDivElement>(null);
const { overlayProps, underlayProps } = useOverlay( const { overlayProps, underlayProps } = useOverlay(
{ {
onClose: () => { onClose: onClose,
setFilterTerm(undefined);
setOpen(false);
},
isDismissable: true, isDismissable: true,
isOpen, isOpen,
shouldCloseOnInteractOutside: (element) => { shouldCloseOnInteractOutside: (element) => {
@ -66,56 +68,46 @@ export function DataSourceDropdown(props: DataSourceDropdownProps) {
return ( return (
<div className={styles.container}> <div className={styles.container}>
<div tabIndex={0} onFocus={openDropdown} role={'button'} className={styles.trigger} onClick={openDropdown}>
<Input
className={isOpen ? undefined : styles.input}
prefix={
filterTerm && isOpen ? (
<DataSourceLogoPlaceHolder />
) : (
<DataSourceLogo dataSource={currentDataSourceInstanceSettings} />
)
}
suffix={<Icon name={isOpen ? 'search' : 'angle-down'} />}
placeholder={dataSourceLabel(currentDataSourceInstanceSettings)}
onFocus={openDropdown}
onClick={openDropdown}
onChange={(e) => {
setFilterTerm(e.currentTarget.value);
}}
ref={setMarkerElement}
></Input>
</div>
{isOpen ? ( {isOpen ? (
<FocusScope contain autoFocus restoreFocus> <Portal>
<Input <div {...underlayProps} />
prefix={ <div ref={ref} {...overlayProps} {...dialogProps}>
filterTerm ? ( <PickerContent
<DataSourceLogoPlaceHolder /> filterTerm={filterTerm}
) : ( onChange={(ds: DataSourceInstanceSettings<DataSourceJsonData>) => {
<DataSourceLogo dataSource={currentDataSourceInstanceSettings} /> onClose();
) onChange(ds);
} }}
suffix={<Icon name={filterTerm ? 'search' : 'angle-down'} />} onClose={onClose}
placeholder={dataSourceLabel(currentDataSourceInstanceSettings)} current={currentDataSourceInstanceSettings}
onChange={(e) => { style={popper.styles.popper}
setFilterTerm(e.currentTarget.value); ref={setSelectorElement}
}} {...restProps}
ref={setMarkerElement} onDismiss={onClose}
></Input> ></PickerContent>
<Portal> </div>
<div {...underlayProps} /> </Portal>
<div ref={ref} {...overlayProps} {...dialogProps}> ) : null}
<PickerContent
filterTerm={filterTerm}
onChange={(ds: DataSourceInstanceSettings<DataSourceJsonData>) => {
setFilterTerm(undefined);
setOpen(false);
onChange(ds);
}}
onClose={() => {
setOpen(false);
}}
current={currentDataSourceInstanceSettings}
style={popper.styles.popper}
ref={setSelectorElement}
{...restProps}
onDismiss={() => {}}
></PickerContent>
</div>
</Portal>
</FocusScope>
) : (
<div className={styles.trigger} onClick={openDropdown}>
<Input
className={styles.input}
prefix={<DataSourceLogo dataSource={currentDataSourceInstanceSettings} />}
suffix={<Icon name="angle-down" />}
value={dataSourceLabel(currentDataSourceInstanceSettings)}
onFocus={openDropdown}
/>
</div>
)}
</div> </div>
); );
} }
@ -132,6 +124,9 @@ function getStylesDropdown(theme: GrafanaTheme2) {
input { input {
cursor: pointer; cursor: pointer;
} }
input::placeholder {
color: ${theme.colors.text.primary};
}
`, `,
}; };
} }