UserGroupUserFormByInput.jsx 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. import React from 'react';
  2. import PropTypes from 'prop-types';
  3. import { withTranslation } from 'react-i18next';
  4. import { AsyncTypeahead } from 'react-bootstrap-typeahead';
  5. import { debounce } from 'throttle-debounce';
  6. import { createSubscribedElement } from '../../UnstatedUtils';
  7. import AppContainer from '../../../services/AppContainer';
  8. import UserGroupDetailContainer from '../../../services/UserGroupDetailContainer';
  9. import { toastSuccess, toastError } from '../../../util/apiNotification';
  10. class UserGroupUserFormByInput extends React.Component {
  11. constructor(props) {
  12. super(props);
  13. this.state = {
  14. input: '',
  15. // TDOO GW-665 fetch users
  16. applicableUsers: ['hoge', 'huga'],
  17. isLoading: false,
  18. searchError: null,
  19. };
  20. this.xss = window.xss;
  21. this.onInputChange = this.onInputChange.bind(this);
  22. this.addUserBySubmit = this.addUserBySubmit.bind(this);
  23. this.validateForm = this.validateForm.bind(this);
  24. this.handleChange = this.handleChange.bind(this);
  25. this.handleSearch = this.handleSearch.bind(this);
  26. this.onKeyDown = this.onKeyDown.bind(this);
  27. this.renderMenuItemChildren = this.renderMenuItemChildren.bind(this);
  28. this.searchUserDebounce = debounce(1000, this.searchUser);
  29. }
  30. /**
  31. * input user name to add to the group
  32. * @param {string} input
  33. */
  34. onInputChange(input) {
  35. this.setState({ input });
  36. // this.props.onInputChange(text);
  37. if (input === '') {
  38. this.setState({ applicableUsers: [] });
  39. }
  40. }
  41. async addUserBySubmit(e) {
  42. e.preventDefault();
  43. const { input } = this.state;
  44. try {
  45. await this.props.userGroupDetailContainer.addUserByUsername(input);
  46. toastSuccess(`Added "${this.xss.process(input)}" to "${this.xss.process(this.props.userGroupDetailContainer.state.userGroup.name)}"`);
  47. this.setState({ input: '' });
  48. }
  49. catch (err) {
  50. toastError(new Error(`Unable to add "${this.xss.process(input)}" to "${this.xss.process(this.props.userGroupDetailContainer.state.userGroup.name)}"`));
  51. }
  52. }
  53. validateForm() {
  54. return this.state.input !== '';
  55. }
  56. searchUser() {
  57. // TODO GW-665 fetch users
  58. this.setState({ isLoading: false });
  59. }
  60. /**
  61. * Reflect when forecast is clicked
  62. * @param {string} input
  63. */
  64. handleChange(input) {
  65. this.setState({ input });
  66. }
  67. handleSearch(keyword) {
  68. if (keyword === '') {
  69. return;
  70. }
  71. this.setState({ isLoading: true });
  72. this.searchUserDebounce();
  73. }
  74. onKeyDown(event) {
  75. // 13 is Enter key
  76. if (event.keyCode === 13) {
  77. this.addUserBySubmit();
  78. }
  79. }
  80. getEmptyLabel() {
  81. return (this.state.searchError !== null) && 'Error on searching.';
  82. }
  83. renderMenuItemChildren(option, props, index) {
  84. const user = option;
  85. return (
  86. <span>
  87. {user}
  88. </span>
  89. );
  90. }
  91. render() {
  92. const { t } = this.props;
  93. const inputProps = { autoComplete: 'off' };
  94. return (
  95. <form className="form-inline" onSubmit={this.addUserBySubmit}>
  96. <div className="form-group">
  97. <AsyncTypeahead
  98. {...this.props}
  99. id="name-typeahead-asynctypeahead"
  100. ref={(c) => { this.typeahead = c }}
  101. inputProps={inputProps}
  102. isLoading={this.state.isLoading}
  103. labelKey="name"
  104. minLength={0}
  105. options={this.state.applicableUsers} // Search result (Some page names)
  106. emptyLabel={this.getEmptyLabel()}
  107. searchText={(this.state.isLoading ? 'Searching...' : this.getEmptyLabel())}
  108. align="left"
  109. onChange={this.handleChange}
  110. onSearch={this.handleSearch}
  111. onInputChange={this.onInputChange}
  112. onKeyDown={this.onKeyDown}
  113. renderMenuItemChildren={this.renderMenuItemChildren}
  114. caseSensitive={false}
  115. />
  116. </div>
  117. <button type="submit" className="btn btn-sm btn-success" disabled={!this.validateForm()}>{ t('add') }</button>
  118. </form>
  119. );
  120. }
  121. }
  122. UserGroupUserFormByInput.propTypes = {
  123. t: PropTypes.func.isRequired, // i18next
  124. appContainer: PropTypes.instanceOf(AppContainer).isRequired,
  125. userGroupDetailContainer: PropTypes.instanceOf(UserGroupDetailContainer).isRequired,
  126. };
  127. /**
  128. * Wrapper component for using unstated
  129. */
  130. const UserGroupUserFormByInputWrapper = (props) => {
  131. return createSubscribedElement(UserGroupUserFormByInput, props, [AppContainer, UserGroupDetailContainer]);
  132. };
  133. export default withTranslation()(UserGroupUserFormByInputWrapper);