[MM-52846] Migrate "components/autocomplete_selector.jsx" and tests to Typescript (#25056)

* Migrate AutocompleteSelector and tests to ts

* Remove commented code

* More cleanup

* Implemented suggestions

* Revert formatting change

* Remove default value and add undefined check

* Remove optional from props with default values

* Unify "Selected" type
Этот коммит содержится в:
janlengyel
2023-10-26 15:28:27 +02:00
коммит произвёл GitHub
родитель cee4411355
Коммит 18bc5536f4
5 изменённых файлов: 65 добавлений и 54 удалений

Просмотреть файл

@@ -52,7 +52,7 @@ describe('components/widgets/settings/AutocompleteSelector', () => {
});
test('check snapshot with value prop and changing focus', () => {
const wrapper = shallow(
const wrapper = shallow<AutocompleteSelector>(
<AutocompleteSelector
providers={[]}
label='some label'
@@ -96,7 +96,7 @@ describe('components/widgets/settings/AutocompleteSelector', () => {
</div>
`);
wrapper.instance().onChange({target: {value: 'value from input'}});
wrapper.instance().onChange(({target: {value: 'value from input'} as HTMLInputElement}));
wrapper.instance().onFocus();
expect(wrapper).toMatchInlineSnapshot(`
@@ -136,7 +136,7 @@ describe('components/widgets/settings/AutocompleteSelector', () => {
test('onSelected', () => {
const onSelected = jest.fn();
const wrapper = shallow(
const wrapper = shallow<AutocompleteSelector>(
<AutocompleteSelector
label='some label'
value='some value'
@@ -145,7 +145,7 @@ describe('components/widgets/settings/AutocompleteSelector', () => {
/>,
);
const selected = {text: 'sometext', value: 'somevalue'};
const selected = {text: 'sometext', value: 'somevalue', id: '', username: '', display_name: ''};
wrapper.instance().handleSelected(selected);
expect(onSelected).toHaveBeenCalledTimes(1);

Просмотреть файл

@@ -1,39 +1,62 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import PropTypes from 'prop-types';
import React from 'react';
import type {Channel} from '@mattermost/types/channels';
import type {UserProfile} from '@mattermost/types/users';
import SuggestionBox from 'components/suggestion/suggestion_box';
import SuggestionList from 'components/suggestion/suggestion_list';
export default class AutocompleteSelector extends React.PureComponent {
static propTypes = {
providers: PropTypes.array.isRequired,
value: PropTypes.string.isRequired,
onSelected: PropTypes.func,
label: PropTypes.node,
labelClassName: PropTypes.string,
inputClassName: PropTypes.string,
helpText: PropTypes.node,
placeholder: PropTypes.string,
footer: PropTypes.node,
disabled: PropTypes.bool,
toggleFocus: PropTypes.func,
listComponent: PropTypes.elementType,
listPosition: PropTypes.string,
};
import type ModalSuggestionList from './suggestion/modal_suggestion_list';
import type Provider from './suggestion/provider';
export type Option = {
text: string;
value: string;
};
export type Selected = Option | UserProfile | Channel
type Props = {
id: string;
providers: Provider[];
value: string;
onSelected?: (selected: Selected) => void;
label?: React.ReactNode | string;
labelClassName: string;
inputClassName: string;
helpText?: React.ReactNode | string;
placeholder?: string;
footer?: Node;
disabled?: boolean;
toggleFocus?: ((focus: boolean) => void) | null;
listComponent: typeof SuggestionList | typeof ModalSuggestionList;
listPosition: string;
};
type State = {
input: string;
focused?: boolean;
};
type ChangeEvent = {
target: HTMLInputElement;
}
export default class AutocompleteSelector extends React.PureComponent<Props, State> {
static defaultProps = {
value: '',
id: '',
value: '',
labelClassName: '',
inputClassName: '',
listComponent: SuggestionList,
listPosition: 'top',
};
constructor(props) {
suggestionRef?: HTMLElement;
constructor(props: Props) {
super(props);
this.state = {
@@ -41,7 +64,7 @@ export default class AutocompleteSelector extends React.PureComponent {
};
}
onChange = (e) => {
onChange = (e: ChangeEvent) => {
if (!e || !e.target) {
return;
}
@@ -49,7 +72,7 @@ export default class AutocompleteSelector extends React.PureComponent {
this.setState({input: e.target.value});
};
handleSelected = (selected) => {
handleSelected = (selected: Selected) => {
this.setState({input: ''});
if (this.props.onSelected) {
@@ -63,7 +86,7 @@ export default class AutocompleteSelector extends React.PureComponent {
});
};
setSuggestionRef = (ref) => {
setSuggestionRef = (ref: HTMLElement) => {
this.suggestionRef = ref;
};

Просмотреть файл

@@ -7,10 +7,12 @@ import {FormattedMessage} from 'react-intl';
import type {UserAutocomplete} from '@mattermost/types/autocomplete';
import type {Channel} from '@mattermost/types/channels';
import type {ServerError} from '@mattermost/types/errors';
import type {UserProfile} from '@mattermost/types/users';
import type {ActionResult} from 'mattermost-redux/types/actions';
import AutocompleteSelector from 'components/autocomplete_selector';
import type {Option, Selected} from 'components/autocomplete_selector';
import GenericChannelProvider from 'components/suggestion/generic_channel_provider';
import GenericUserProvider from 'components/suggestion/generic_user_provider';
import MenuActionProvider from 'components/suggestion/menu_action_provider';
@@ -52,14 +54,6 @@ type State = {
value: string;
}
type Selected = {
id: string;
username: string;
display_name: string;
value: string;
text: string;
}
export default class DialogElement extends React.PureComponent<Props, State> {
private providers: Provider[];
@@ -94,14 +88,17 @@ export default class DialogElement extends React.PureComponent<Props, State> {
const {name, dataSource} = this.props;
if (dataSource === 'users') {
this.props.onChange(name, selected.id);
this.setState({value: selected.username});
const user = selected as UserProfile;
this.props.onChange(name, user.id);
this.setState({value: user.username});
} else if (dataSource === 'channels') {
this.props.onChange(name, selected.id);
this.setState({value: selected.display_name});
const channel = selected as Channel;
this.props.onChange(name, channel.id);
this.setState({value: channel.display_name});
} else {
this.props.onChange(name, selected.value);
this.setState({value: selected.text});
const option = selected as Option;
this.props.onChange(name, option.value);
this.setState({value: option.text});
}
};

Просмотреть файл

@@ -13,6 +13,7 @@ import {AppBindingLocations, AppCallResponseTypes} from 'mattermost-redux/consta
import type {ActionResult} from 'mattermost-redux/types/actions';
import AutocompleteSelector from 'components/autocomplete_selector';
import type {Option, Selected} from 'components/autocomplete_selector';
import PostContext from 'components/post_view/post_context';
import MenuActionProvider from 'components/suggestion/menu_action_provider';
@@ -20,11 +21,6 @@ import {createCallContext} from 'utils/apps';
import type {HandleBindingClick, OpenAppsModal, PostEphemeralCallResponseForPost} from 'types/apps';
type Option = {
text: string;
value: string;
};
type Props = {
intl: IntlShape;
post: Post;
@@ -84,8 +80,9 @@ class SelectBinding extends React.PureComponent<Props, State> {
this.state = {};
}
handleSelected = async (selected: Option) => {
if (!selected) {
handleSelected = async (selected: Selected) => {
//this component expects selected to be of type Option
if (!selected || !('value' in selected)) {
return;
}

Просмотреть файл

@@ -7,6 +7,7 @@ import type {Channel} from '@mattermost/types/channels';
import type {UserProfile} from '@mattermost/types/users';
import AutocompleteSelector from 'components/autocomplete_selector';
import type {Option, Selected} from 'components/autocomplete_selector';
import PostContext from 'components/post_view/post_context';
import GenericChannelProvider from 'components/suggestion/generic_channel_provider';
import GenericUserProvider from 'components/suggestion/generic_user_provider';
@@ -14,15 +15,8 @@ import MenuActionProvider from 'components/suggestion/menu_action_provider';
import type {OwnProps, PropsFromRedux} from './index';
type Option = {
text: string;
value: string;
};
type Provider = GenericUserProvider | GenericChannelProvider | MenuActionProvider;
type Selected = Option | UserProfile | Channel;
export type Props = OwnProps & PropsFromRedux;
type State = {