[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
Этот коммит содержится в:
@@ -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 = {
|
||||
|
||||
Ссылка в новой задаче
Block a user