You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Form.tsx 3.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2019 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. import * as React from 'react';
  21. import { translate } from 'sonar-ui-common/helpers/l10n';
  22. import Select from 'sonar-ui-common/components/controls/Select';
  23. interface Props {
  24. allGates: T.QualityGate[];
  25. gate?: T.QualityGate;
  26. onChange: (oldGate?: number, newGate?: number) => Promise<void>;
  27. }
  28. interface State {
  29. loading: boolean;
  30. }
  31. interface Option {
  32. isDefault?: boolean;
  33. label: string;
  34. value: string;
  35. }
  36. export default class Form extends React.PureComponent<Props, State> {
  37. mounted = false;
  38. state: State = { loading: false };
  39. componentDidMount() {
  40. this.mounted = true;
  41. }
  42. componentWillUnmount() {
  43. this.mounted = false;
  44. }
  45. stopLoading = () => {
  46. if (this.mounted) {
  47. this.setState({ loading: false });
  48. }
  49. };
  50. handleChange = (option: { value: string }) => {
  51. const { gate } = this.props;
  52. const isSet = gate == null && option.value != null;
  53. const isUnset = gate != null && option.value == null;
  54. const isChanged = gate != null && gate.id !== Number(option.value);
  55. const hasChanged = isSet || isUnset || isChanged;
  56. if (hasChanged) {
  57. this.setState({ loading: true });
  58. this.props
  59. .onChange(gate && gate.id, Number(option.value))
  60. .then(this.stopLoading, this.stopLoading);
  61. }
  62. };
  63. renderGateName = (option: { isDefault?: boolean; label: string }) => {
  64. if (option.isDefault) {
  65. return (
  66. <span>
  67. <strong>{translate('default')}</strong>
  68. {': '}
  69. {option.label}
  70. </span>
  71. );
  72. }
  73. return <span>{option.label}</span>;
  74. };
  75. renderSelect() {
  76. const { gate, allGates } = this.props;
  77. const options: Option[] = allGates.map(gate => ({
  78. value: String(gate.id),
  79. label: gate.name,
  80. isDefault: gate.isDefault
  81. }));
  82. return (
  83. <Select
  84. clearable={false}
  85. disabled={this.state.loading}
  86. onChange={this.handleChange}
  87. optionRenderer={this.renderGateName}
  88. options={options}
  89. style={{ width: 300 }}
  90. value={gate && String(gate.id)}
  91. valueRenderer={this.renderGateName}
  92. />
  93. );
  94. }
  95. render() {
  96. return (
  97. <div>
  98. {this.renderSelect()}
  99. {this.state.loading && <i className="spinner spacer-left" />}
  100. </div>
  101. );
  102. }
  103. }