Fix search date suggestion crash (#26476)

* Fix search date suggestion crash

* Fix tests

* Improve snapshots

* Fix snapshot
This commit is contained in:
Daniel Espino García 2024-03-21 09:50:12 +01:00 committed by GitHub
parent 1383d51436
commit 8e43d45b3f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 31 additions and 100 deletions

View File

@ -24,4 +24,4 @@ function mapStateToProps(state: GlobalState) {
};
}
export default connect(mapStateToProps)(SearchDateSuggestion);
export default connect(mapStateToProps, null, null, {forwardRef: true})(SearchDateSuggestion);

View File

@ -15,9 +15,9 @@ import 'react-day-picker/dist/style.css';
type Props = SuggestionProps<never> & {
currentDate?: Date;
handleEscape: () => void;
handleEscape?: () => void;
locale: string;
preventClose: () => void;
preventClose?: () => void;
}
export default class SearchDateSuggestion extends React.PureComponent<Props> {
@ -36,7 +36,7 @@ export default class SearchDateSuggestion extends React.PureComponent<Props> {
if (Keyboard.isKeyPressed(e, Constants.KeyCodes.DOWN) && document.activeElement?.id === 'searchBox') {
this.setState({datePickerFocused: true});
} else if (Keyboard.isKeyPressed(e, Constants.KeyCodes.ESCAPE)) {
this.props.handleEscape();
this.props.handleEscape?.();
}
};

View File

@ -10,6 +10,7 @@ import Popover from 'components/widgets/popover';
import Constants from 'utils/constants';
import type {UserProfile} from './command_provider/app_command_parser/app_command_parser_dependencies';
import type {Props} from './suggestion_list';
import SuggestionList from './suggestion_list';
interface Item extends UserProfile {
@ -18,34 +19,6 @@ interface Item extends UserProfile {
name: string;
}
interface Props {
ariaLiveRef?: React.RefObject<HTMLDivElement>;
inputRef?: React.RefObject<HTMLInputElement>;
open: boolean;
position?: 'top' | 'bottom';
renderDividers?: string[];
renderNoResults?: boolean;
onCompleteWord: (term: string, matchedPretext: string, e?: React.KeyboardEventHandler<HTMLDivElement>) => boolean;
preventClose?: () => void;
onItemHover: (term: string) => void;
pretext: string;
cleared: boolean;
matchedPretext: string[];
items: any[];
terms: string[];
selection: string;
components: Array<React.FunctionComponent<any>>;
wrapperHeight?: number;
// suggestionBoxAlgn is an optional object that can be passed to align the SuggestionList with the keyboard caret
// as the user is typing.
suggestionBoxAlgn?: {
lineHeight: number;
pixelsToMoveX: number;
pixelsToMoveY: number;
};
}
export default class SearchSuggestionList extends SuggestionList {
popoverRef: React.RefObject<BSPopover>;
itemsContainerRef: React.RefObject<HTMLDivElement>;

View File

@ -6,8 +6,12 @@ import React, {memo} from 'react';
import Popover from 'components/widgets/popover';
type SuggestionItem = {
date: string;
label: string;
}
type SuggestionItemProps = {
key: string;
ref: string;
item: SuggestionItem;
term: string;
matchedPretext: string;
@ -24,7 +28,7 @@ type Props = {
terms: string[];
preventClose: () => void;
handleEscape: () => void;
components: Array<React.ComponentType<SuggestionItem>>;
components: Array<React.ComponentType<SuggestionItemProps>>;
}
const SuggestionDate = ({
@ -54,7 +58,6 @@ const SuggestionDate = ({
>
<Component
key={term}
ref={term}
item={item}
term={term}
matchedPretext={matchedPretext[0]}

View File

@ -12,7 +12,7 @@ import LoadingSpinner from 'components/widgets/loading/loading_spinner';
import {Constants} from 'utils/constants';
import {isEmptyObject} from 'utils/utils';
interface Props {
export interface Props {
ariaLiveRef?: React.RefObject<HTMLDivElement>;
inputRef?: React.RefObject<HTMLDivElement>;
open: boolean;
@ -28,7 +28,7 @@ interface Props {
items: any[];
terms: string[];
selection: string;
components: Array<React.FunctionComponent<any>>;
components: Array<React.ComponentType<any>>;
wrapperHeight?: number;
// suggestionBoxAlgn is an optional object that can be passed to align the SuggestionList with the keyboard caret

View File

@ -56,7 +56,6 @@ exports[`component/user_group_popover should match snapshot 1`] = `
},
}
}
canManageGroup={true}
group={
Object {
"allow_reference": true,
@ -79,49 +78,8 @@ exports[`component/user_group_popover should match snapshot 1`] = `
searchTerm=""
showUserOverlay={[MockFunction]}
>
<Memo(Popover)
actions={
Object {
"openModal": [MockFunction],
"searchProfiles": [MockFunction],
"setPopoverSearchTerm": [MockFunction] {
"calls": Array [
Array [
"",
],
],
"results": Array [
Object {
"type": "return",
"value": undefined,
},
],
},
}
}
canManageGroup={true}
group={
Object {
"allow_reference": true,
"create_at": 1,
"delete_at": 0,
"description": "",
"display_name": "group_display_name",
"has_syncables": false,
"id": "group1",
"member_count": 15,
"name": "group_name",
"remote_id": "",
"scheme_admin": false,
"source": "",
"update_at": 1,
}
}
hide={[MockFunction]}
<Popover
id="user-group-popover"
returnFocus={[MockFunction]}
searchTerm=""
showUserOverlay={[MockFunction]}
>
<Popover
bsClass="popover"
@ -2126,7 +2084,7 @@ exports[`component/user_group_popover should match snapshot 1`] = `
</div>
</div>
</Popover>
</Memo(Popover)>
</Popover>
</Memo(UserGroupPopover)>
</Router>
</BrowserRouter>

View File

@ -2,6 +2,7 @@
// See LICENSE.txt for license information.
import type {ReactWrapper} from 'enzyme';
import type {ComponentProps} from 'react';
import React from 'react';
import {Provider} from 'react-redux';
import {BrowserRouter} from 'react-router-dom';
@ -100,10 +101,9 @@ describe('component/user_group_popover', () => {
},
};
const baseProps = {
const baseProps: ComponentProps<typeof UserGroupPopover> = {
searchTerm: '',
group: group1,
canManageGroup: true,
showUserOverlay: jest.fn(),
hide: jest.fn(),
returnFocus: jest.fn(),

View File

@ -64,16 +64,14 @@ export type Props = {
};
}
const UserGroupPopover = (props: Props) => {
const {
group,
actions,
hide,
returnFocus,
searchTerm,
showUserOverlay,
} = props;
const UserGroupPopover = ({
actions,
group,
hide,
returnFocus,
searchTerm,
showUserOverlay,
}: Props) => {
const {formatMessage} = useIntl();
const closeRef = useRef<HTMLButtonElement>(null);
@ -183,7 +181,6 @@ const UserGroupPopover = (props: Props) => {
return (
<Popover
{...props}
id='user-group-popover'
>
{tabCatcher}

View File

@ -19,10 +19,9 @@ interface Props {
style?: React.CSSProperties;
onMouseOut?: React.MouseEventHandler<BSPopover>; // didn't find a better way to satisfy typing, so for now we have a slight 'bootstrap leakage'
onMouseOver?: React.MouseEventHandler<BSPopover>;
ref?: React.Ref<BSPopover>;
}
const Popover = ({
const Popover = React.forwardRef<BSPopover, Props>(({
placement = 'right',
popoverSize = 'sm',
children,
@ -33,8 +32,7 @@ const Popover = ({
onMouseOver,
className,
style,
ref,
}: Props) => {
}, ref?) => {
return (
<BSPopover
id={id}
@ -45,13 +43,15 @@ const Popover = ({
bsClass='popover'
title={title}
bsSize={popoverSize && SizeMap[popoverSize] as BSSizes} // map our sizes to bootstrap
onMouseOut={onMouseOut!}
onMouseOut={onMouseOut}
onMouseOver={onMouseOver}
ref={ref}
>
{children}
</BSPopover>
);
};
});
Popover.displayName = 'Popover';
export default React.memo(Popover);