ui/job-view/details/tabs/SideBySide.jsx (183 lines of code) (raw):

import React from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faExternalLinkAlt, faSpinner, } from '@fortawesome/free-solid-svg-icons'; import { Table } from 'reactstrap'; import { getJobsUrl } from '../../../helpers/url'; import { notify } from '../../redux/stores/notifications'; import { getData } from '../../../helpers/http'; import Clipboard from '../../../shared/Clipboard'; import SideBySideVideo from './SideBySideVideo'; class SideBySide extends React.PureComponent { constructor(props) { super(props); this.state = { sideBySideLoading: false, sideBySideParams: undefined, }; } componentDidMount() { const { sideBySideParams } = this.state; if (!sideBySideParams) { this.getSideBySideParams(); } } getSideBySideParams() { const { jobDetails } = this.props; this.setState( { sideBySideLoading: true, }, () => { if (jobDetails && jobDetails.length > 3) { const sideBySideParamsPromise = getData(jobDetails[3].url, { 'Access-Control-Allow-Origin': ['*'], }); Promise.all([sideBySideParamsPromise]).then( async ([sideBySideParamsResult]) => { const sideBySideParams = sideBySideParamsResult.data; this.setState( { sideBySideParams, }, async () => { this.setState({ sideBySideLoading: false }); }, ); }, ); } }, ); } render() { const { jobDetails } = this.props; const { sideBySideLoading, sideBySideParams } = this.state; if (!sideBySideParams) { return null; } if (jobDetails.length === 0) { return null; } const beforeJobLink = getJobsUrl({ repo: sideBySideParams.base_branch, revision: sideBySideParams.base_revision, searchStr: [ sideBySideParams.platform, '/opt-', sideBySideParams.test_name, ].join(''), group_state: 'expanded', }); const afterJobLink = getJobsUrl({ repo: sideBySideParams.new_branch, revision: sideBySideParams.new_revision, searchStr: [ sideBySideParams.test_name, '/opt-', sideBySideParams.platform, ].join(''), group_state: 'expanded', }); const videos = { cold: [jobDetails[0], jobDetails[2]], warm: [jobDetails[4], jobDetails[6]], }; return sideBySideLoading ? ( <div className="overlay"> <div> <FontAwesomeIcon icon={faSpinner} pulse className="th-spinner-lg" title="Loading..." /> </div> </div> ) : ( <div> <h3 className="font-size-16 mt-3 mb-2"> <strong>Side by side comparison for test </strong> <code>{sideBySideParams.test_name}</code> <strong> on platform </strong> <code>{sideBySideParams.platform}</code> </h3> <h3 className="font-size-12 mb-2 d-flex flex-column"> <div className="d-flex"> <div className="pt-1"> <strong>Before: </strong> <span> {sideBySideParams.base_branch} /{' '} {sideBySideParams.base_revision.substring(0, 12)} </span> <a title={`Open revision ${sideBySideParams.base_revision} on ${sideBySideParams.base_branch}`} href={beforeJobLink} target="_blank" rel="noopener noreferrer" className="text-monospace ml-1" > (<FontAwesomeIcon icon={faExternalLinkAlt} className="mr-2" /> job) </a> </div> <Clipboard description="job link" text={beforeJobLink} /> <div className="pt-1 ml-1"> <strong>After: </strong> <span> {sideBySideParams.new_branch} /{' '} {sideBySideParams.new_revision.substring(0, 12)} </span> <a title={`Open revision ${sideBySideParams.new_revision} on ${sideBySideParams.new_branch}`} href={afterJobLink} target="_blank" rel="noopener noreferrer" className="text-monospace ml-1" > (<FontAwesomeIcon icon={faExternalLinkAlt} className="mr-2" /> job) </a> </div> <Clipboard description="job link" text={afterJobLink} /> </div> </h3> {jobDetails && ( <Table> <tbody> <tr className="d-flex"> <td> <div className="d-flex mb-1"> <span>Select video</span> </div> <SideBySideVideo videos={videos.cold} /> </td> <td> <div className="d-flex mb-1"> <span>Select video</span> </div> <SideBySideVideo videos={videos.warm} /> </td> </tr> </tbody> </Table> )} </div> ); } } SideBySide.propTypes = { jobDetails: PropTypes.arrayOf(PropTypes.shape({})), }; SideBySide.defaultProps = { jobDetails: [], }; const mapStateToProps = (state) => ({ decisionTaskMap: state.pushes.decisionTaskMap, }); const mapDispatchToProps = { notify }; export default connect(mapStateToProps, mapDispatchToProps)(SideBySide);