ui/perfherder/compare/RetriggerModal.jsx (198 lines of code) (raw):

import React from 'react'; import PropTypes from 'prop-types'; import { Col, Form, Input, InputGroup, InputGroupAddon, InputGroupText, Button, Modal, ModalHeader, ModalBody, ModalFooter, } from 'reactstrap'; export default class RetriggerModal extends React.Component { constructor(props) { super(props); this.state = { baseRetriggerTimes: this.getInitialValue(true), newRetriggerTimes: this.getInitialValue(), invalidInput: false, }; } onOpened = () => { this.setState({ baseRetriggerTimes: this.getInitialValue(true), newRetriggerTimes: this.getInitialValue(), invalidInput: false, }); }; onClosed = () => { const { defaultRetriggersValue } = this.props; this.setState({ baseRetriggerTimes: defaultRetriggersValue, newRetriggerTimes: defaultRetriggersValue, invalidInput: false, }); }; getInitialValue = (isBaseline = false) => { const { defaultRetriggersValue, isBaseAggregate, currentRetriggerRow, } = this.props; let initialValue = defaultRetriggersValue; if (isBaseline) { if (isBaseAggregate || !currentRetriggerRow.originalRetriggerableJobId) { initialValue = 0; } } else if (!currentRetriggerRow.newRetriggerableJobId) { initialValue = 0; } return initialValue; }; getInputTitle = (isBaseline = false) => { const { isBaseAggregate, currentRetriggerRow } = this.props; let disableReason; if (isBaseline) { if (isBaseAggregate) { disableReason = 'base revision is aggregate'; } else if (!currentRetriggerRow.originalRetriggerableJobId) { disableReason = 'there are no jobs to retrigger'; } } else if (!currentRetriggerRow.newRetriggerableJobId) { disableReason = 'there are no jobs to retrigger'; } return disableReason === undefined ? disableReason : `Disabled input because ${disableReason}`; }; isValueValid = (value) => { const { maxRetriggersValue } = this.props; const regex = /^\d+$/; if (!value.match(regex)) { return false; } const parsedValue = parseInt(value, 10); return parsedValue >= 0 && parsedValue <= maxRetriggersValue; }; handleChange = (event) => { const inputName = event.target.name; const updates = { [inputName]: 0, invalidInput: false, }; if (this.isValueValid(event.target.value)) { updates[inputName] = event.target.value; } else { updates.invalidInput = true; } this.setState(updates); }; onRetriggerClick = (event) => { const { updateAndClose } = this.props; const { baseRetriggerTimes, newRetriggerTimes } = this.state; updateAndClose(event, { baseRetriggerTimes: Number.isInteger(baseRetriggerTimes) ? baseRetriggerTimes : parseInt(baseRetriggerTimes, 10), newRetriggerTimes: Number.isInteger(newRetriggerTimes) ? newRetriggerTimes : parseInt(newRetriggerTimes, 10), }); }; render() { const { showModal, toggle, isBaseAggregate, maxRetriggersValue, currentRetriggerRow, } = this.props; const { invalidInput } = this.state; return ( <Modal isOpen={showModal} onOpened={this.onOpened} onClosed={this.onClosed} > <ModalHeader toggle={toggle}>Retrigger Jobs</ModalHeader> <Form> <ModalBody> <div className="row"> <Col className="col-xs-10 col-sm-6 col-md-6 col-lg-6 form-inline"> <InputGroup title={this.getInputTitle(true)}> <InputGroupAddon addonType="prepend"> <InputGroupText>Base revision:</InputGroupText> </InputGroupAddon> <Input data-testid="input baseRetriggerTimes" defaultValue={this.getInitialValue(true)} min={0} max={maxRetriggersValue} onChange={this.handleChange} name="baseRetriggerTimes" type="number" disabled={ isBaseAggregate || !currentRetriggerRow.originalRetriggerableJobId } /> </InputGroup> </Col> <Col className="col-xs-10 col-sm-6 col-md-6 col-lg-6 form-inline"> <InputGroup title={this.getInputTitle()}> <InputGroupAddon addonType="prepend"> <InputGroupText>New revision:</InputGroupText> </InputGroupAddon> <Input data-testid="input newRetriggerTimes" defaultValue={this.getInitialValue()} min={0} max={maxRetriggersValue} onChange={this.handleChange} name="newRetriggerTimes" type="number" disabled={!currentRetriggerRow.newRetriggerableJobId} /> </InputGroup> </Col> </div> <div className="text-center"> {invalidInput && ( <p className="text-danger pt-2 text-wrap"> *Inputs should be numbers in 0 - {maxRetriggersValue} range </p> )} </div> </ModalBody> <ModalFooter> <Button color="info" onClick={this.onRetriggerClick} disabled={invalidInput} > Retrigger </Button> </ModalFooter> </Form> </Modal> ); } } RetriggerModal.propTypes = { showModal: PropTypes.bool.isRequired, toggle: PropTypes.func.isRequired, updateAndClose: PropTypes.func.isRequired, isBaseAggregate: PropTypes.bool.isRequired, currentRetriggerRow: PropTypes.shape({}), defaultRetriggersValue: PropTypes.number, maxRetriggersValue: PropTypes.number, }; RetriggerModal.defaultProps = { defaultRetriggersValue: 5, maxRetriggersValue: 10, currentRetriggerRow: {}, };