Skip to content
This repository was archived by the owner on Jun 6, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License

import React from 'react';
import { DefaultButton, ColorClassNames } from 'office-ui-fabric-react';
import yaml from 'js-yaml';
import PropTypes from 'prop-types';

export const YamlEditExportConfig = React.memo(({ protocolYaml }) => {
const _exportFile = (data, filename, type) => {
const file = new Blob([data], { type: type });
if (window.navigator.msSaveOrOpenBlob) {
// IE10+
window.navigator.msSaveOrOpenBlob(file, filename);
} else {
// Others
const a = document.createElement('a');
const url = URL.createObjectURL(file);
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
setTimeout(function() {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 0);
}
};

const _exportYaml = async event => {
event.preventDefault();
try {
_exportFile(
protocolYaml,
(yaml.safeLoad(protocolYaml).name || 'job') + '.yaml',
'text/yaml',
);
} catch (err) {
alert(err);
}
};

return (
<DefaultButton
styles={{
root: [ColorClassNames.neutralTertiaryAltBackground],
rootHovered: [ColorClassNames.neutralTertiaryBackground],
}}
onClick={_exportYaml}
>
Export
</DefaultButton>
);
});

YamlEditExportConfig.propTypes = {
protocolYaml: PropTypes.string.isRequired,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License

import React from 'react';
import {
FontWeights,
DefaultButton,
Label,
ColorClassNames,
} from 'office-ui-fabric-react';
import PropTypes from 'prop-types';

import { JobProtocol } from '../../models/job-protocol';

export const YamlEditImportConfig = React.memo(({ onChange }) => {
const _importFile = event => {
event.preventDefault();
const files = event.target.files;
if (!files || !files[0]) {
return;
}
const fileReader = new FileReader();
fileReader.addEventListener('load', () => {
const text = String(fileReader.result);
const valid = JobProtocol.validateFromYaml(text);
if (valid) {
alert(`Yaml file is invalid. ${valid}`);
return;
}
try {
onChange(text);
} catch (err) {
alert(err.message);
}
});
fileReader.readAsText(files[0]);
};

return (
<DefaultButton>
<Label
styles={{
root: [
{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
width: '100%',
cursor: 'pointer',
fontWeight: FontWeights.semibold,
},
ColorClassNames.neutralTertiaryAltBackground,
ColorClassNames.neutralTertiaryBackgroundHover,
],
}}
>
{'Import'}
<input
type='file'
style={{
width: '1px',
height: '1px',
opacity: '.0001',
}}
accept='.yml,.yaml'
onChange={_importFile}
/>
</Label>
</DefaultButton>
);
});

YamlEditImportConfig.propTypes = {
onChange: PropTypes.func,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License

import React from 'react';
import PropTypes from 'prop-types';

import { Text, Stack, StackItem } from 'office-ui-fabric-react';

import { YamlEditExportConfig } from './yamledit-export-config';
import { YamlEditImportConfig } from './yamledit-import-config';

export const YamlEditTopBar = ({ protocolYaml, onChange }) => {
return (
<Stack horizontal horizontalAlign='space-between' padding='0 m'>
<Stack horizontal gap='m' verticalAlign='baseline'>
<StackItem>
<Text variant='xLarge' styles={{ root: { fontWeight: 'semibold' } }}>
Config Editor
</Text>
</StackItem>
</Stack>
<Stack horizontal gap='s1'>
<YamlEditExportConfig protocolYaml={protocolYaml} />
<YamlEditImportConfig onChange={onChange} />
</Stack>
</Stack>
);
};

YamlEditTopBar.propTypes = {
protocolYaml: PropTypes.string,
onChange: PropTypes.func,
history: PropTypes.object,
};
5 changes: 5 additions & 0 deletions src/webportal/src/app/job-submission/job-submission.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { HashRouter as Router, Route } from 'react-router-dom';
import { Fabric } from 'office-ui-fabric-react';

import { JobSubmissionPage } from './job-submission-page';
import { YamlEditPage } from './yaml-edit-page';
import JobWizard from './job-wizard';

const App = () => {
Expand Down Expand Up @@ -63,6 +64,10 @@ const App = () => {
/>
)}
/>
<Route
path='/yaml-edit'
render={({ history }) => <YamlEditPage history={history} />}
/>
</Router>
</Fabric>
);
Expand Down
39 changes: 3 additions & 36 deletions src/webportal/src/app/job-submission/job-wizard.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import React, { useCallback, useEffect, useState } from 'react';
import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import {
getTheme,
Expand All @@ -16,7 +16,6 @@ import Card from '../components/card';
import { ReactComponent as IconSingle } from '../../assets/img/job-wizard-single.svg';
import { ReactComponent as IconDistributed } from '../../assets/img/job-wizard-distributed.svg';
import { ReactComponent as IconUpload } from '../../assets/img/job-wizard-upload.svg';
import { JobProtocol } from './models/job-protocol';
import { SpinnerLoading } from '../components/loading';

const WizardButton = ({ children, onClick }) => {
Expand Down Expand Up @@ -65,33 +64,8 @@ WizardButton.propTypes = {
};

const JobWizard = ({ setYamlText, history }) => {
const uploadFile = React.createRef();
const [loading, setLoading] = useState(true);

const importFile = useCallback(event => {
event.preventDefault();
const files = event.target.files;
if (!files || !files[0]) {
return;
}
const fileReader = new FileReader();
fileReader.addEventListener('load', () => {
const text = fileReader.result;
const valid = JobProtocol.validateFromYaml(text);
if (valid) {
alert(`Yaml file is invalid. ${valid}`);
return;
}
try {
setYamlText(text);
history.push('/general');
} catch (err) {
alert(err.message);
}
});
fileReader.readAsText(files[0]);
});

// redirect if job clone or local storage
useEffect(() => {
const params = new URLSearchParams(window.location.search);
Expand Down Expand Up @@ -133,25 +107,18 @@ const JobWizard = ({ setYamlText, history }) => {
<Stack horizontalAlign='center' gap={50}>
<WizardButton
onClick={() => {
uploadFile.current.click();
history.push('/yaml-edit');
}}
>
<IconUpload />
</WizardButton>
<input
type='file'
ref={uploadFile}
style={{ display: 'none' }}
accept='.yml,.yaml'
onChange={importFile}
/>
<div
style={{
fontSize: FontSizes.large,
fontWeight: FontWeights.semibold,
}}
>
Import Config
Config Editor
</div>
</Stack>
<Stack horizontalAlign='center' gap={50}>
Expand Down
Loading