Make sure big lists of suggestions don't expand outside of the viewport (#33520)

* Add CustomScrollbar to data links suggestions

* Make sure to scroll to selected suggestion
This commit is contained in:
Oscar Kilhed 2021-04-30 09:52:34 +02:00 committed by GitHub
parent deb9ead72f
commit aaca022df6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 26 additions and 9 deletions

View File

@ -18,6 +18,7 @@ import { SCHEMA } from '../../utils/slate';
import { useStyles2 } from '../../themes';
import { DataLinkBuiltInVars, GrafanaThemeV2, VariableOrigin, VariableSuggestion } from '@grafana/data';
import { getInputStyles } from '../Input/Input';
import CustomScrollbar from '../CustomScrollbar/CustomScrollbar';
const modulo = (a: number, n: number) => a - n * Math.floor(a / n);
@ -81,6 +82,12 @@ export const DataLinkInput: React.FC<DataLinkInputProps> = memo(
const stateRef = useRef({ showingSuggestions, suggestions, suggestionsIndex, linkUrl, onChange });
stateRef.current = { showingSuggestions, suggestions, suggestionsIndex, linkUrl, onChange };
// Used to get the height of the suggestion elements in order to scroll to them.
const activeRef = useRef<HTMLDivElement>(null);
const activeIndexPosition = useMemo(() => getElementPosition(activeRef.current, suggestionsIndex), [
suggestionsIndex,
]);
// SelectionReference is used to position the variables suggestion relatively to current DOM selection
const selectionRef = useMemo(() => new SelectionReference(), []);
@ -172,12 +179,15 @@ export const DataLinkInput: React.FC<DataLinkInputProps> = memo(
{({ ref, style, placement }) => {
return (
<div ref={ref} style={style} data-placement={placement}>
<DataLinkSuggestions
suggestions={stateRef.current.suggestions}
onSuggestionSelect={onVariableSelect}
onClose={() => setShowingSuggestions(false)}
activeIndex={suggestionsIndex}
/>
<CustomScrollbar scrollTop={activeIndexPosition} autoHeightMax="300px">
<DataLinkSuggestions
activeRef={activeRef}
suggestions={stateRef.current.suggestions}
onSuggestionSelect={onVariableSelect}
onClose={() => setShowingSuggestions(false)}
activeIndex={suggestionsIndex}
/>
</CustomScrollbar>
</div>
);
}}
@ -208,3 +218,7 @@ export const DataLinkInput: React.FC<DataLinkInputProps> = memo(
);
DataLinkInput.displayName = 'DataLinkInput';
function getElementPosition(suggestionElement: HTMLElement | null, activeIndex: number) {
return (suggestionElement?.clientHeight ?? 0) * activeIndex;
}

View File

@ -7,6 +7,7 @@ import { List } from '../index';
import { useStyles2 } from '../../themes';
interface DataLinkSuggestionsProps {
activeRef?: React.RefObject<HTMLDivElement>;
suggestions: VariableSuggestion[];
activeIndex: number;
onSuggestionSelect: (suggestion: VariableSuggestion) => void;
@ -23,7 +24,6 @@ const getStyles = (theme: GrafanaThemeV2) => {
`,
wrapper: css`
background: ${theme.colors.background.primary};
z-index: 1;
width: 250px;
box-shadow: 0 5px 10px 0 ${theme.shadows.z1};
`,
@ -100,10 +100,11 @@ DataLinkSuggestions.displayName = 'DataLinkSuggestions';
interface DataLinkSuggestionsListProps extends DataLinkSuggestionsProps {
label: string;
activeIndexOffset: number;
activeRef?: React.RefObject<HTMLDivElement>;
}
const DataLinkSuggestionsList: React.FC<DataLinkSuggestionsListProps> = React.memo(
({ activeIndex, activeIndexOffset, label, onClose, onSuggestionSelect, suggestions }) => {
({ activeIndex, activeIndexOffset, label, onClose, onSuggestionSelect, suggestions, activeRef: selectedRef }) => {
const styles = useStyles2(getStyles);
return (
@ -112,9 +113,11 @@ const DataLinkSuggestionsList: React.FC<DataLinkSuggestionsListProps> = React.me
className={styles.list}
items={suggestions}
renderItem={(item, index) => {
const isActive = index + activeIndexOffset === activeIndex;
return (
<div
className={cx(styles.item, index + activeIndexOffset === activeIndex && styles.activeItem)}
className={cx(styles.item, isActive && styles.activeItem)}
ref={isActive ? selectedRef : undefined}
onClick={() => {
onSuggestionSelect(item);
}}