diff --git a/web/chat/typeahead-tooltip.react.js b/web/chat/typeahead-tooltip.react.js --- a/web/chat/typeahead-tooltip.react.js +++ b/web/chat/typeahead-tooltip.react.js @@ -8,6 +8,7 @@ import type { InputState } from '../input/input-state'; import { + getTypeaheadOverlayScroll, getTypeaheadTooltipActions, getTypeaheadTooltipButtons, getTypeaheadTooltipPosition, @@ -36,6 +37,8 @@ setChosenPositionInOverlay, ] = React.useState(0); + const overlayRef = React.useRef(); + React.useEffect(() => { setChosenPositionInOverlay(0); }, [suggestedUsers]); @@ -144,6 +147,17 @@ inputState.setTypeaheadState, ]); + React.useEffect(() => { + const current = overlayRef.current; + if (current) { + const newScrollTop = getTypeaheadOverlayScroll( + current.scrollTop, + chosenPositionInOverlay, + ); + current.scrollTo(0, newScrollTop); + } + }, [chosenPositionInOverlay]); + if (suggestedUsers.length === 0) { return null; } @@ -154,7 +168,11 @@ }); return ( -
+
{tooltipButtons}
); diff --git a/web/utils/typeahead-utils.js b/web/utils/typeahead-utils.js --- a/web/utils/typeahead-utils.js +++ b/web/utils/typeahead-utils.js @@ -154,6 +154,30 @@ }); } +function getTypeaheadOverlayScroll( + currentScrollTop: number, + chosenActionPosition: number, +): number { + const upperButtonBoundary = chosenActionPosition * typeaheadStyle.rowHeight; + const lowerButtonBoundary = + (chosenActionPosition + 1) * typeaheadStyle.rowHeight; + + if (upperButtonBoundary < currentScrollTop) { + return upperButtonBoundary; + } else if ( + lowerButtonBoundary - typeaheadStyle.tooltipMaxHeight > + currentScrollTop + ) { + return ( + lowerButtonBoundary + + typeaheadStyle.tooltipVerticalPadding - + typeaheadStyle.tooltipMaxHeight + ); + } + + return currentScrollTop; +} + function getTypeaheadTooltipPosition( textarea: HTMLTextAreaElement, actionsLength: number, @@ -189,5 +213,6 @@ getCaretOffsets, getTypeaheadTooltipActions, getTypeaheadTooltipButtons, + getTypeaheadOverlayScroll, getTypeaheadTooltipPosition, }; diff --git a/web/utils/typeahead-utils.test.js b/web/utils/typeahead-utils.test.js new file mode 100644 --- /dev/null +++ b/web/utils/typeahead-utils.test.js @@ -0,0 +1,32 @@ +// @flow + +import { typeaheadStyle } from '../chat/chat-constants'; +import { getTypeaheadOverlayScroll } from './typeahead-utils'; + +describe('getTypeaheadOverlayScroll', () => { + it( + 'should return the same scroll position when' + + 'it is already scrolled to the top and changing between 2nd button', + () => expect(getTypeaheadOverlayScroll(0, 1)).toEqual(0), + ); + + it( + 'should scroll down when it is scrolled to the top' + + 'and changing to button out of screen', + () => + expect(getTypeaheadOverlayScroll(0, 6)).toEqual( + (6 + 1) * typeaheadStyle.rowHeight + + typeaheadStyle.tooltipVerticalPadding - + typeaheadStyle.tooltipMaxHeight, + ), + ); + + it( + 'should scroll up when it is scrolled somewhere down' + + 'and changing to button out of screen to the top', + () => + expect(getTypeaheadOverlayScroll(500, 3)).toEqual( + 3 * typeaheadStyle.rowHeight, + ), + ); +});