Skip to content

Commit

Permalink
Convert FileUpload to functional
Browse files Browse the repository at this point in the history
  • Loading branch information
mshriver committed Jan 22, 2025
1 parent e2d7c84 commit c0e3952
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 102 deletions.
2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
"scripts": {
"start": "serve -s build -l tcp://0.0.0.0:8080",
"build": "./bin/write-version-file.cjs && react-scripts build",
"test": "./bin/write-version-file.cjs && react-scripts test --transformIgnorePatterns \"node_modules/(?!@patternfly)/\" && cypress run --component",
"test": "./bin/write-version-file.cjs && react-scripts test --transformIgnorePatterns \"node_modules/(?!@patternfly)/\"",
"eject": "react-scripts eject",
"devserver": "./bin/write-version-file.cjs && CI=true react-scripts start",
"lint": "eslint"
Expand Down
13 changes: 0 additions & 13 deletions frontend/src/components/FileUpload.spec.cy.js

This file was deleted.

153 changes: 73 additions & 80 deletions frontend/src/components/fileupload.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { useContext, useRef, useState } from 'react';
import PropTypes from 'prop-types';
import UploadIcon from '@patternfly/react-icons/dist/esm/icons/upload-icon';

Expand All @@ -12,93 +12,82 @@ import ToastWrapper from './toast-wrapper';
import { Settings } from '../settings';
import { ALERT_TIMEOUT } from '../constants';

export class FileUpload extends React.Component {
// TODO: refactor to functional
// TODO: Consider explicit project selection for upload instead of inferred from context
// TODO: support multiple upload
static contextType = IbutsuContext;
static propTypes = {
url: PropTypes.string.isRequired,
name: PropTypes.string,
children: PropTypes.node,
}

constructor(props) {
super(props);
this.state = {
url: props.url,
name: props.name ? props.name : 'file',
importId: null,
intervalId: null
};
this.inputRef = React.createRef();
this.importToastRef = React.createRef();
this.intervalId = React.createRef();
const FileUpload = (props) => {
const context = useContext(IbutsuContext);

const [importId, setImportId] = useState();

const inputRef = useRef();
const importToastRef = useRef();
const intervalId = useRef();

const name = props?.name ? props.name : 'file';

}

onClick = () => {
this.inputRef.current.click();
function onClick() {
inputRef.current.click();
}

onFileChange = (e) => {
function onFileChange(e) {
let files = e.target.files || e.dataTransfer.files;
if (files.length > 0) {
this.uploadFile(files[0]);
uploadFile(files[0]);
// Clear the upload field
this.inputRef.current.value = '';
inputRef.current.value = '';
}
}

checkImportStatus = (importId) => {
const { primaryObject } = this.context;

HttpClient.get([Settings.serverUrl, 'import', importId])
.then(response => HttpClient.handleResponse(response))
.then(data => {
if (data['status'] === 'done') {
clearInterval(this.intervalId.current);
this.importId = null;
let action = null;
if (data.metadata.run_id) {
const RunButton = () => (
<AlertActionLink component='a' href={'/project/' + (data.metadata.project_id || primaryObject.id) + '/runs/' + data.metadata.run_id}>
Go to Run
</AlertActionLink>
)
action = <RunButton />;
}
toast.update(this.importToastRef.current,
{
data: {
type:'success',
title:'Import Complete',
message: `${data.filename} has been successfully imported as run ${data.metadata.run_id}`,
action: action
},
type: 'success',
autoClose: ALERT_TIMEOUT
function checkImportStatus() {
const { primaryObject } = context;
if(importId) {
HttpClient.get([Settings.serverUrl, 'import', importId])
.then(response => HttpClient.handleResponse(response))
.then(data => {
if (data['status'] === 'done') {
clearInterval(intervalId.current);
setImportId();
let action = null;
if (data.metadata.run_id) {
const RunButton = () => (
<AlertActionLink component='a' href={'/project/' + (data.metadata.project_id || primaryObject.id) + '/runs/' + data.metadata.run_id}>
Go to Run
</AlertActionLink>
)
action = <RunButton />;
}
)
toast.update(importToastRef.current,
{
data: {
type:'success',
title:'Import Complete',
message: `${data.filename} has been successfully imported as run ${data.metadata.run_id}`,
action: action
},
type: 'success',
autoClose: ALERT_TIMEOUT
}
)

}
});
}
});
}
}

uploadFile = (file) => {
function uploadFile(file) {
const files = {};
const { primaryObject } = this.context;
files[this.state.name] = file;
const { primaryObject } = context;
files[name] = file;

HttpClient.upload(
this.state.url,
Settings.serverUrl+'/import',
files,
{'project': primaryObject?.id}
)
.then((response) => HttpClient.handleResponse(response, 'response'))
.then(data => {
data.json().then((importObject) => {
this.importToastRef.current = toast(<ToastWrapper />,
importToastRef.current = toast(<ToastWrapper />,
{
data: {
type: 'info',
Expand All @@ -109,11 +98,12 @@ export class FileUpload extends React.Component {
theme: getDarkTheme() ? 'dark' : 'light'
}
);
this.intervalId.current = setInterval(() => {this.checkImportStatus(importObject['id'])}, 5000);
setImportId(importObject['id']);
intervalId.current = setInterval(checkImportStatus, 5000);
})
})
.catch(error => {
toast.update(this.importToastRef.current,
toast.update(importToastRef.current,
{
data: {
type: 'danger',
Expand All @@ -126,18 +116,21 @@ export class FileUpload extends React.Component {
});
}

render() {
const { children } = this.props;
const { primaryObject } = this.context;
return (
<React.Fragment>
<input type="file" multiple={false} style={{display: 'none'}} onChange={this.onFileChange} ref={this.inputRef} />
<Tooltip content="Upload a result archive to the selected project.">
<Button variant={ButtonVariant.tertiary} icon={<Icon><UploadIcon/></Icon>} onClick={this.onClick} isAriaDisabled={!primaryObject}>
{children}
</Button>
</Tooltip>
</React.Fragment>
);
}
const {primaryObject} = context;
return (
<React.Fragment>
<input type="file" multiple={false} style={{display: 'none'}} onChange={onFileChange} ref={inputRef} />
<Tooltip content="Upload xUnit XML or Ibutsuresult archive to the selected project.">
<Button variant={ButtonVariant.tertiary} icon={<Icon><UploadIcon/></Icon>} onClick={onClick} isAriaDisabled={!primaryObject}>
Import</Button>
</Tooltip>
</React.Fragment>
);

}

FileUpload.propTypes = {
name: PropTypes.string,
};

export default FileUpload;
9 changes: 3 additions & 6 deletions frontend/src/components/ibutsu-header.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ import TimesIcon from '@patternfly/react-icons/dist/esm/icons/times-icon';
import QuestionCircleIcon from '@patternfly/react-icons/dist/esm/icons/question-circle-icon';


import { FileUpload, UserDropdown } from '../components';
import { UserDropdown } from '../components';
import FileUpload from './fileupload';
import { VERSION } from '../constants';
import { HttpClient } from '../services/http';
import { Settings } from '../settings';
Expand Down Expand Up @@ -247,11 +248,7 @@ function IbutsuHeader (props) {
icon={<Icon><QuestionCircleIcon /></Icon>} />
</ToolbarItem>
<ToolbarItem spacer={{ default: 'spacerSm' }}>
<FileUpload
name="importFile"
url={`${Settings.serverUrl}/import`}
title="Import xUnit XML or Ibutsu Archive"
>Import</FileUpload>
<FileUpload name="importFile"/>
</ToolbarItem>
<ToolbarItem spacer={{ default: 'spacerSm' }}>
<Button
Expand Down
1 change: 0 additions & 1 deletion frontend/src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ export { ClassifyFailuresTable } from './classify-failures';
export { DeleteModal } from './delete-modal';
export { DownloadButton } from './download-button';
export { EmptyObject } from './empty-object';
export { FileUpload } from './fileupload';
export { FilterTable, MetaFilter } from './filtertable';
export { IbutsuPage } from './ibutsu-page';
export { MultiValueInput } from './multivalueinput';
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/toast-wrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import PropTypes from 'prop-types';


const ToastWrapper = ({data}) => (
<Alert key={data.key} customIcon={Icon} title={data.title} actionLinks={data.action}>
<Alert key={data.key} customIcon={<Icon/>} title={data.title} actionLinks={data.action}>
{data.message}
</Alert>
);
Expand Down

0 comments on commit c0e3952

Please sign in to comment.