Convert AddressPickerDialog to TS

Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com>
This commit is contained in:
Šimon Brandner 2021-07-20 11:26:33 +02:00
parent 156901ce62
commit a747bbbae7
No known key found for this signature in database
GPG key ID: 55C211A1226CB17D

View file

@ -18,7 +18,6 @@ limitations under the License.
*/ */
import React, { createRef } from 'react'; import React, { createRef } from 'react';
import PropTypes from 'prop-types';
import { sleep } from "matrix-js-sdk/src/utils"; import { sleep } from "matrix-js-sdk/src/utils";
import { _t, _td } from '../../../languageHandler'; import { _t, _td } from '../../../languageHandler';
@ -34,6 +33,7 @@ import { abbreviateUrl } from '../../../utils/UrlUtils';
import { Key } from "../../../Keyboard"; import { Key } from "../../../Keyboard";
import { Action } from "../../../dispatcher/actions"; import { Action } from "../../../dispatcher/actions";
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
import AddressSelector from '../elements/AddressSelector';
const TRUNCATE_QUERY_LIST = 40; const TRUNCATE_QUERY_LIST = 40;
const QUERY_USER_DIRECTORY_DEBOUNCE_MS = 200; const QUERY_USER_DIRECTORY_DEBOUNCE_MS = 200;
@ -44,27 +44,54 @@ const addressTypeName = {
'email': _td("email address"), 'email': _td("email address"),
}; };
interface IProps {
title: string;
description?: JSX.Element;
// Extra node inserted after picker input, dropdown and errors
extraNode?: JSX.Element;
value?: string;
placeholder?: ((validAddressTypes: any) => string) | string;
roomId?: string;
button?: string;
focus?: boolean;
validAddressTypes?; // FIXME: UserAddressType should be an interface
onFinished: (success: boolean, list?) => void; // FIXME: UserAddressType should be an interface
groupId?: string;
// The type of entity to search for. Default: 'user'.
pickerType?: 'user' | 'room';
// Whether the current user should be included in the addresses returned. Only
// applicable when pickerType is `user`. Default: false.
includeSelf?: boolean;
}
interface IState {
// Whether to show an error message because of an invalid address
invalidAddressError: boolean;
// List of UserAddressType objects representing
// the list of addresses we're going to invite
selectedList; // FIXME: UserAddressType should be an interface
// Whether a search is ongoing
busy: boolean;
// An error message generated during the user directory search
searchError: string;
// Whether the server supports the user_directory API
serverSupportsUserDirectory: boolean;
// The query being searched for
query: string;
// List of UserAddressType objects representing the set of
// auto-completion results for the current search query.
suggestedList; // FIXME: UserAddressType should be an interface
// List of address types initialised from props, but may change while the
// dialog is open and represents the supported list of address types at this time.
validAddressTypes;
}
@replaceableComponent("views.dialogs.AddressPickerDialog") @replaceableComponent("views.dialogs.AddressPickerDialog")
export default class AddressPickerDialog extends React.Component { export default class AddressPickerDialog extends React.Component<IProps, IState> {
static propTypes = { private textinput = createRef<HTMLTextAreaElement>();
title: PropTypes.string.isRequired, private addressSelector = createRef<AddressSelector>();
description: PropTypes.node, private queryChangedDebouncer: NodeJS.Timeout;
// Extra node inserted after picker input, dropdown and errors private cancelThreepidLookup: () => void;
extraNode: PropTypes.node,
value: PropTypes.string,
placeholder: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
roomId: PropTypes.string,
button: PropTypes.string,
focus: PropTypes.bool,
validAddressTypes: PropTypes.arrayOf(PropTypes.oneOf(addressTypes)),
onFinished: PropTypes.func.isRequired,
groupId: PropTypes.string,
// The type of entity to search for. Default: 'user'.
pickerType: PropTypes.oneOf(['user', 'room']),
// Whether the current user should be included in the addresses returned. Only
// applicable when pickerType is `user`. Default: false.
includeSelf: PropTypes.bool,
};
static defaultProps = { static defaultProps = {
value: "", value: "",
@ -74,11 +101,9 @@ export default class AddressPickerDialog extends React.Component {
includeSelf: false, includeSelf: false,
}; };
constructor(props) { constructor(props: IProps) {
super(props); super(props);
this._textinput = createRef();
let validAddressTypes = this.props.validAddressTypes; let validAddressTypes = this.props.validAddressTypes;
// Remove email from validAddressTypes if no IS is configured. It may be added at a later stage by the user // Remove email from validAddressTypes if no IS is configured. It may be added at a later stage by the user
if (!MatrixClientPeg.get().getIdentityServerUrl() && validAddressTypes.includes("email")) { if (!MatrixClientPeg.get().getIdentityServerUrl() && validAddressTypes.includes("email")) {
@ -86,24 +111,13 @@ export default class AddressPickerDialog extends React.Component {
} }
this.state = { this.state = {
// Whether to show an error message because of an invalid address
invalidAddressError: false, invalidAddressError: false,
// List of UserAddressType objects representing
// the list of addresses we're going to invite
selectedList: [], selectedList: [],
// Whether a search is ongoing
busy: false, busy: false,
// An error message generated during the user directory search
searchError: null, searchError: null,
// Whether the server supports the user_directory API
serverSupportsUserDirectory: true, serverSupportsUserDirectory: true,
// The query being searched for
query: "", query: "",
// List of UserAddressType objects representing the set of
// auto-completion results for the current search query.
suggestedList: [], suggestedList: [],
// List of address types initialised from props, but may change while the
// dialog is open and represents the supported list of address types at this time.
validAddressTypes, validAddressTypes,
}; };
} }
@ -111,11 +125,11 @@ export default class AddressPickerDialog extends React.Component {
componentDidMount() { componentDidMount() {
if (this.props.focus) { if (this.props.focus) {
// Set the cursor at the end of the text input // Set the cursor at the end of the text input
this._textinput.current.value = this.props.value; this.textinput.current.value = this.props.value;
} }
} }
getPlaceholder() { private getPlaceholder(): string {
const { placeholder } = this.props; const { placeholder } = this.props;
if (typeof placeholder === "string") { if (typeof placeholder === "string") {
return placeholder; return placeholder;
@ -124,23 +138,23 @@ export default class AddressPickerDialog extends React.Component {
return placeholder(this.state.validAddressTypes); return placeholder(this.state.validAddressTypes);
} }
onButtonClick = () => { private onButtonClick = (): void => {
let selectedList = this.state.selectedList.slice(); let selectedList = this.state.selectedList.slice();
// Check the text input field to see if user has an unconverted address // Check the text input field to see if user has an unconverted address
// If there is and it's valid add it to the local selectedList // If there is and it's valid add it to the local selectedList
if (this._textinput.current.value !== '') { if (this.textinput.current.value !== '') {
selectedList = this._addAddressesToList([this._textinput.current.value]); selectedList = this.addAddressesToList([this.textinput.current.value]);
if (selectedList === null) return; if (selectedList === null) return;
} }
this.props.onFinished(true, selectedList); this.props.onFinished(true, selectedList);
}; };
onCancel = () => { private onCancel = (): void => {
this.props.onFinished(false); this.props.onFinished(false);
}; };
onKeyDown = e => { private onKeyDown = (e: React.KeyboardEvent): void => {
const textInput = this._textinput.current ? this._textinput.current.value : undefined; const textInput = this.textinput.current ? this.textinput.current.value : undefined;
if (e.key === Key.ESCAPE) { if (e.key === Key.ESCAPE) {
e.stopPropagation(); e.stopPropagation();
@ -149,15 +163,15 @@ export default class AddressPickerDialog extends React.Component {
} else if (e.key === Key.ARROW_UP) { } else if (e.key === Key.ARROW_UP) {
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
if (this.addressSelector) this.addressSelector.moveSelectionUp(); if (this.addressSelector.current) this.addressSelector.current.moveSelectionUp();
} else if (e.key === Key.ARROW_DOWN) { } else if (e.key === Key.ARROW_DOWN) {
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
if (this.addressSelector) this.addressSelector.moveSelectionDown(); if (this.addressSelector.current) this.addressSelector.current.moveSelectionDown();
} else if (this.state.suggestedList.length > 0 && [Key.COMMA, Key.ENTER, Key.TAB].includes(e.key)) { } else if (this.state.suggestedList.length > 0 && [Key.COMMA, Key.ENTER, Key.TAB].includes(e.key)) {
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
if (this.addressSelector) this.addressSelector.chooseSelection(); if (this.addressSelector.current) this.addressSelector.current.chooseSelection();
} else if (textInput.length === 0 && this.state.selectedList.length && e.key === Key.BACKSPACE) { } else if (textInput.length === 0 && this.state.selectedList.length && e.key === Key.BACKSPACE) {
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
@ -169,17 +183,17 @@ export default class AddressPickerDialog extends React.Component {
// if there's nothing in the input box, submit the form // if there's nothing in the input box, submit the form
this.onButtonClick(); this.onButtonClick();
} else { } else {
this._addAddressesToList([textInput]); this.addAddressesToList([textInput]);
} }
} else if (textInput && (e.key === Key.COMMA || e.key === Key.TAB)) { } else if (textInput && (e.key === Key.COMMA || e.key === Key.TAB)) {
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
this._addAddressesToList([textInput]); this.addAddressesToList([textInput]);
} }
}; };
onQueryChanged = ev => { private onQueryChanged = (ev: React.ChangeEvent): void => {
const query = ev.target.value; const query = (ev.target as HTMLTextAreaElement).value;
if (this.queryChangedDebouncer) { if (this.queryChangedDebouncer) {
clearTimeout(this.queryChangedDebouncer); clearTimeout(this.queryChangedDebouncer);
} }
@ -188,17 +202,17 @@ export default class AddressPickerDialog extends React.Component {
this.queryChangedDebouncer = setTimeout(() => { this.queryChangedDebouncer = setTimeout(() => {
if (this.props.pickerType === 'user') { if (this.props.pickerType === 'user') {
if (this.props.groupId) { if (this.props.groupId) {
this._doNaiveGroupSearch(query); this.doNaiveGroupSearch(query);
} else if (this.state.serverSupportsUserDirectory) { } else if (this.state.serverSupportsUserDirectory) {
this._doUserDirectorySearch(query); this.doUserDirectorySearch(query);
} else { } else {
this._doLocalSearch(query); this.doLocalSearch(query);
} }
} else if (this.props.pickerType === 'room') { } else if (this.props.pickerType === 'room') {
if (this.props.groupId) { if (this.props.groupId) {
this._doNaiveGroupRoomSearch(query); this.doNaiveGroupRoomSearch(query);
} else { } else {
this._doRoomSearch(query); this.doRoomSearch(query);
} }
} else { } else {
console.error('Unknown pickerType', this.props.pickerType); console.error('Unknown pickerType', this.props.pickerType);
@ -213,7 +227,7 @@ export default class AddressPickerDialog extends React.Component {
} }
}; };
onDismissed = index => () => { private onDismissed = (index: number) => () => {
const selectedList = this.state.selectedList.slice(); const selectedList = this.state.selectedList.slice();
selectedList.splice(index, 1); selectedList.splice(index, 1);
this.setState({ this.setState({
@ -221,25 +235,21 @@ export default class AddressPickerDialog extends React.Component {
suggestedList: [], suggestedList: [],
query: "", query: "",
}); });
if (this._cancelThreepidLookup) this._cancelThreepidLookup(); if (this.cancelThreepidLookup) this.cancelThreepidLookup();
}; };
onClick = index => () => { private onSelected = (index: number): void => {
this.onSelected(index);
};
onSelected = index => {
const selectedList = this.state.selectedList.slice(); const selectedList = this.state.selectedList.slice();
selectedList.push(this._getFilteredSuggestions()[index]); selectedList.push(this.getFilteredSuggestions()[index]);
this.setState({ this.setState({
selectedList, selectedList,
suggestedList: [], suggestedList: [],
query: "", query: "",
}); });
if (this._cancelThreepidLookup) this._cancelThreepidLookup(); if (this.cancelThreepidLookup) this.cancelThreepidLookup();
}; };
_doNaiveGroupSearch(query) { private doNaiveGroupSearch(query: string): void {
const lowerCaseQuery = query.toLowerCase(); const lowerCaseQuery = query.toLowerCase();
this.setState({ this.setState({
busy: true, busy: true,
@ -260,7 +270,7 @@ export default class AddressPickerDialog extends React.Component {
display_name: u.displayname, display_name: u.displayname,
}); });
}); });
this._processResults(results, query); this.processResults(results, query);
}).catch((err) => { }).catch((err) => {
console.error('Error whilst searching group rooms: ', err); console.error('Error whilst searching group rooms: ', err);
this.setState({ this.setState({
@ -273,7 +283,7 @@ export default class AddressPickerDialog extends React.Component {
}); });
} }
_doNaiveGroupRoomSearch(query) { private doNaiveGroupRoomSearch(query: string): void {
const lowerCaseQuery = query.toLowerCase(); const lowerCaseQuery = query.toLowerCase();
const results = []; const results = [];
GroupStore.getGroupRooms(this.props.groupId).forEach((r) => { GroupStore.getGroupRooms(this.props.groupId).forEach((r) => {
@ -289,13 +299,13 @@ export default class AddressPickerDialog extends React.Component {
name: r.name || r.canonical_alias, name: r.name || r.canonical_alias,
}); });
}); });
this._processResults(results, query); this.processResults(results, query);
this.setState({ this.setState({
busy: false, busy: false,
}); });
} }
_doRoomSearch(query) { private doRoomSearch(query: string): void {
const lowerCaseQuery = query.toLowerCase(); const lowerCaseQuery = query.toLowerCase();
const rooms = MatrixClientPeg.get().getRooms(); const rooms = MatrixClientPeg.get().getRooms();
const results = []; const results = [];
@ -346,13 +356,13 @@ export default class AddressPickerDialog extends React.Component {
return a.rank - b.rank; return a.rank - b.rank;
}); });
this._processResults(sortedResults, query); this.processResults(sortedResults, query);
this.setState({ this.setState({
busy: false, busy: false,
}); });
} }
_doUserDirectorySearch(query) { private doUserDirectorySearch(query: string): void {
this.setState({ this.setState({
busy: true, busy: true,
query, query,
@ -366,7 +376,7 @@ export default class AddressPickerDialog extends React.Component {
if (this.state.query !== query) { if (this.state.query !== query) {
return; return;
} }
this._processResults(resp.results, query); this.processResults(resp.results, query);
}).catch((err) => { }).catch((err) => {
console.error('Error whilst searching user directory: ', err); console.error('Error whilst searching user directory: ', err);
this.setState({ this.setState({
@ -377,7 +387,7 @@ export default class AddressPickerDialog extends React.Component {
serverSupportsUserDirectory: false, serverSupportsUserDirectory: false,
}); });
// Do a local search immediately // Do a local search immediately
this._doLocalSearch(query); this.doLocalSearch(query);
} }
}).then(() => { }).then(() => {
this.setState({ this.setState({
@ -386,7 +396,7 @@ export default class AddressPickerDialog extends React.Component {
}); });
} }
_doLocalSearch(query) { private doLocalSearch(query: string): void {
this.setState({ this.setState({
query, query,
searchError: null, searchError: null,
@ -407,10 +417,10 @@ export default class AddressPickerDialog extends React.Component {
avatar_url: user.avatarUrl, avatar_url: user.avatarUrl,
}); });
}); });
this._processResults(results, query); this.processResults(results, query);
} }
_processResults(results, query) { private processResults(results, query: string): void {
const suggestedList = []; const suggestedList = [];
results.forEach((result) => { results.forEach((result) => {
if (result.room_id) { if (result.room_id) {
@ -465,20 +475,20 @@ export default class AddressPickerDialog extends React.Component {
address: query, address: query,
isKnown: false, isKnown: false,
}); });
if (this._cancelThreepidLookup) this._cancelThreepidLookup(); if (this.cancelThreepidLookup) this.cancelThreepidLookup();
if (addrType === 'email') { if (addrType === 'email') {
this._lookupThreepid(addrType, query); this.lookupThreepid(addrType, query);
} }
} }
this.setState({ this.setState({
suggestedList, suggestedList,
invalidAddressError: false, invalidAddressError: false,
}, () => { }, () => {
if (this.addressSelector) this.addressSelector.moveSelectionTop(); if (this.addressSelector.current) this.addressSelector.current.moveSelectionTop();
}); });
} }
_addAddressesToList(addressTexts) { private addAddressesToList(addressTexts): void {
const selectedList = this.state.selectedList.slice(); const selectedList = this.state.selectedList.slice();
let hasError = false; let hasError = false;
@ -518,17 +528,17 @@ export default class AddressPickerDialog extends React.Component {
query: "", query: "",
invalidAddressError: hasError ? true : this.state.invalidAddressError, invalidAddressError: hasError ? true : this.state.invalidAddressError,
}); });
if (this._cancelThreepidLookup) this._cancelThreepidLookup(); if (this.cancelThreepidLookup) this.cancelThreepidLookup();
return hasError ? null : selectedList; return hasError ? null : selectedList;
} }
async _lookupThreepid(medium, address) { private async lookupThreepid(medium, address): Promise<string> {
let cancelled = false; let cancelled = false;
// Note that we can't safely remove this after we're done // Note that we can't safely remove this after we're done
// because we don't know that it's the same one, so we just // because we don't know that it's the same one, so we just
// leave it: it's replacing the old one each time so it's // leave it: it's replacing the old one each time so it's
// not like they leak. // not like they leak.
this._cancelThreepidLookup = function() { this.cancelThreepidLookup = function() {
cancelled = true; cancelled = true;
}; };
@ -570,7 +580,7 @@ export default class AddressPickerDialog extends React.Component {
} }
} }
_getFilteredSuggestions() { private getFilteredSuggestions() { // FIXME: UserAddressType should be an interface
// map addressType => set of addresses to avoid O(n*m) operation // map addressType => set of addresses to avoid O(n*m) operation
const selectedAddresses = {}; const selectedAddresses = {};
this.state.selectedList.forEach(({ address, addressType }) => { this.state.selectedList.forEach(({ address, addressType }) => {
@ -584,15 +594,15 @@ export default class AddressPickerDialog extends React.Component {
}); });
} }
_onPaste = e => { private onPaste = (e: React.ClipboardEvent): void => {
// Prevent the text being pasted into the textarea // Prevent the text being pasted into the textarea
e.preventDefault(); e.preventDefault();
const text = e.clipboardData.getData("text"); const text = e.clipboardData.getData("text");
// Process it as a list of addresses to add instead // Process it as a list of addresses to add instead
this._addAddressesToList(text.split(/[\s,]+/)); this.addAddressesToList(text.split(/[\s,]+/));
}; };
onUseDefaultIdentityServerClick = e => { private onUseDefaultIdentityServerClick = (e: React.MouseEvent): void => {
e.preventDefault(); e.preventDefault();
// Update the IS in account data. Actually using it may trigger terms. // Update the IS in account data. Actually using it may trigger terms.
@ -605,7 +615,7 @@ export default class AddressPickerDialog extends React.Component {
this.setState({ validAddressTypes }); this.setState({ validAddressTypes });
}; };
onManageSettingsClick = e => { private onManageSettingsClick = (e: React.MouseEvent): void => {
e.preventDefault(); e.preventDefault();
dis.fire(Action.ViewUserSettings); dis.fire(Action.ViewUserSettings);
this.onCancel(); this.onCancel();
@ -615,12 +625,11 @@ export default class AddressPickerDialog extends React.Component {
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
const AddressSelector = sdk.getComponent("elements.AddressSelector"); const AddressSelector = sdk.getComponent("elements.AddressSelector");
this.scrollElement = null;
let inputLabel; let inputLabel;
if (this.props.description) { if (this.props.description) {
inputLabel = <div className="mx_AddressPickerDialog_label"> inputLabel = <div className="mx_AddressPickerDialog_label">
<label htmlFor="textinput">{this.props.description}</label> <label htmlFor="textinput">{ this.props.description }</label>
</div>; </div>;
} }
@ -644,10 +653,10 @@ export default class AddressPickerDialog extends React.Component {
query.push( query.push(
<textarea <textarea
key={this.state.selectedList.length} key={this.state.selectedList.length}
onPaste={this._onPaste} onPaste={this.onPaste}
rows="1" rows={1}
id="textinput" id="textinput"
ref={this._textinput} ref={this.textinput}
className="mx_AddressPickerDialog_input" className="mx_AddressPickerDialog_input"
onChange={this.onQueryChanged} onChange={this.onQueryChanged}
placeholder={this.getPlaceholder()} placeholder={this.getPlaceholder()}
@ -656,7 +665,7 @@ export default class AddressPickerDialog extends React.Component {
</textarea>, </textarea>,
); );
const filteredSuggestedList = this._getFilteredSuggestions(); const filteredSuggestedList = this.getFilteredSuggestions();
let error; let error;
let addressSelector; let addressSelector;
@ -675,7 +684,7 @@ export default class AddressPickerDialog extends React.Component {
error = <div className="mx_AddressPickerDialog_error">{ _t("No results") }</div>; error = <div className="mx_AddressPickerDialog_error">{ _t("No results") }</div>;
} else { } else {
addressSelector = ( addressSelector = (
<AddressSelector ref={(ref) => {this.addressSelector = ref;}} <AddressSelector ref={this.addressSelector}
addressList={filteredSuggestedList} addressList={filteredSuggestedList}
showAddress={this.props.pickerType === 'user'} showAddress={this.props.pickerType === 'user'}
onSelected={this.onSelected} onSelected={this.onSelected}
@ -690,7 +699,7 @@ export default class AddressPickerDialog extends React.Component {
&& this.props.validAddressTypes.includes('email')) { && this.props.validAddressTypes.includes('email')) {
const defaultIdentityServerUrl = getDefaultIdentityServerUrl(); const defaultIdentityServerUrl = getDefaultIdentityServerUrl();
if (defaultIdentityServerUrl) { if (defaultIdentityServerUrl) {
identityServer = <div className="mx_AddressPickerDialog_identityServer">{_t( identityServer = <div className="mx_AddressPickerDialog_identityServer">{ _t(
"Use an identity server to invite by email. " + "Use an identity server to invite by email. " +
"<default>Use the default (%(defaultIdentityServerName)s)</default> " + "<default>Use the default (%(defaultIdentityServerName)s)</default> " +
"or manage in <settings>Settings</settings>.", "or manage in <settings>Settings</settings>.",
@ -698,25 +707,25 @@ export default class AddressPickerDialog extends React.Component {
defaultIdentityServerName: abbreviateUrl(defaultIdentityServerUrl), defaultIdentityServerName: abbreviateUrl(defaultIdentityServerUrl),
}, },
{ {
default: sub => <a href="#" onClick={this.onUseDefaultIdentityServerClick}>{sub}</a>, default: sub => <a href="#" onClick={this.onUseDefaultIdentityServerClick}>{ sub }</a>,
settings: sub => <a href="#" onClick={this.onManageSettingsClick}>{sub}</a>, settings: sub => <a href="#" onClick={this.onManageSettingsClick}>{ sub }</a>,
}, },
)}</div>; ) }</div>;
} else { } else {
identityServer = <div className="mx_AddressPickerDialog_identityServer">{_t( identityServer = <div className="mx_AddressPickerDialog_identityServer">{ _t(
"Use an identity server to invite by email. " + "Use an identity server to invite by email. " +
"Manage in <settings>Settings</settings>.", "Manage in <settings>Settings</settings>.",
{}, { {}, {
settings: sub => <a href="#" onClick={this.onManageSettingsClick}>{sub}</a>, settings: sub => <a href="#" onClick={this.onManageSettingsClick}>{ sub }</a>,
}, },
)}</div>; ) }</div>;
} }
} }
return ( return (
<BaseDialog className="mx_AddressPickerDialog" onKeyDown={this.onKeyDown} <BaseDialog className="mx_AddressPickerDialog" onKeyDown={this.onKeyDown}
onFinished={this.props.onFinished} title={this.props.title}> onFinished={this.props.onFinished} title={this.props.title}>
{inputLabel} { inputLabel }
<div className="mx_Dialog_content"> <div className="mx_Dialog_content">
<div className="mx_AddressPickerDialog_inputContainer">{ query }</div> <div className="mx_AddressPickerDialog_inputContainer">{ query }</div>
{ error } { error }