Implemented notification & switched to async/await & added file choocer

This commit is contained in:
Weirdo 2021-03-29 01:01:03 +02:00
parent 8305d58933
commit 5ee29023a2
11 changed files with 531 additions and 73 deletions

201
package-lock.json generated
View file

@ -18,33 +18,26 @@
"integrity": "sha512-3eJJ841uKxeV8dcN/2yGEUy+RfgQspPEgQat85umsE1rotuquQ2AbIub4S6j7c50a2d+4myc+zSlnXeIHrOnhQ=="
},
"@babel/core": {
"version": "7.12.3",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.3.tgz",
"integrity": "sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g==",
"version": "7.13.13",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.13.13.tgz",
"integrity": "sha512-1xEs9jZAyKIouOoCmpsgk/I26PoKyvzQ2ixdRpRzfbcp1fL+ozw7TUgdDgwonbTovqRaTfRh50IXuw4QrWO0GA==",
"requires": {
"@babel/code-frame": "^7.10.4",
"@babel/generator": "^7.12.1",
"@babel/helper-module-transforms": "^7.12.1",
"@babel/helpers": "^7.12.1",
"@babel/parser": "^7.12.3",
"@babel/template": "^7.10.4",
"@babel/traverse": "^7.12.1",
"@babel/types": "^7.12.1",
"@babel/code-frame": "^7.12.13",
"@babel/generator": "^7.13.9",
"@babel/helper-compilation-targets": "^7.13.13",
"@babel/helper-module-transforms": "^7.13.12",
"@babel/helpers": "^7.13.10",
"@babel/parser": "^7.13.13",
"@babel/template": "^7.12.13",
"@babel/traverse": "^7.13.13",
"@babel/types": "^7.13.13",
"convert-source-map": "^1.7.0",
"debug": "^4.1.0",
"gensync": "^1.0.0-beta.1",
"gensync": "^1.0.0-beta.2",
"json5": "^2.1.2",
"lodash": "^4.17.19",
"resolve": "^1.3.2",
"semver": "^5.4.1",
"semver": "^6.3.0",
"source-map": "^0.5.0"
},
"dependencies": {
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
}
}
},
"@babel/generator": {
@ -1211,6 +1204,15 @@
"@hapi/hoek": "^8.3.0"
}
},
"@hypnosphi/create-react-context": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/@hypnosphi/create-react-context/-/create-react-context-0.3.1.tgz",
"integrity": "sha512-V1klUed202XahrWJLLOT3EXNeCpFHCcJntdFGI15ntCwau+jfT386w7OFTMaCqOgXUH1fa0w/I1oZs+i/Rfr0A==",
"requires": {
"gud": "^1.0.0",
"warning": "^4.0.3"
}
},
"@istanbuljs/load-nyc-config": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
@ -3870,6 +3872,29 @@
"babel-plugin-transform-react-remove-prop-types": "0.4.24"
},
"dependencies": {
"@babel/core": {
"version": "7.12.3",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.3.tgz",
"integrity": "sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g==",
"requires": {
"@babel/code-frame": "^7.10.4",
"@babel/generator": "^7.12.1",
"@babel/helper-module-transforms": "^7.12.1",
"@babel/helpers": "^7.12.1",
"@babel/parser": "^7.12.3",
"@babel/template": "^7.10.4",
"@babel/traverse": "^7.12.1",
"@babel/types": "^7.12.1",
"convert-source-map": "^1.7.0",
"debug": "^4.1.0",
"gensync": "^1.0.0-beta.1",
"json5": "^2.1.2",
"lodash": "^4.17.19",
"resolve": "^1.3.2",
"semver": "^5.4.1",
"source-map": "^0.5.0"
}
},
"@babel/plugin-proposal-class-properties": {
"version": "7.12.1",
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.12.1.tgz",
@ -4606,6 +4631,11 @@
}
}
},
"classnames": {
"version": "2.2.6",
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz",
"integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q=="
},
"clean-css": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz",
@ -7849,6 +7879,11 @@
"integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=",
"optional": true
},
"gud": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz",
"integrity": "sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw=="
},
"gzip-size": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz",
@ -14469,6 +14504,10 @@
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz",
"integrity": "sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew=="
},
"react-file-picker": {
"version": "git+https://github.com/dantheuber/react-file-picker.git#61c15b0c863987a80aa80c22a9b5caf6b975dc7e",
"from": "git+https://github.com/dantheuber/react-file-picker.git#master"
},
"react-ga": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/react-ga/-/react-ga-3.3.0.tgz",
@ -14479,6 +14518,37 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"react-lifecycles-compat": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
},
"react-notifications-component": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/react-notifications-component/-/react-notifications-component-3.1.0.tgz",
"integrity": "sha512-qq+zgqVIa2zhlw+RvO2QSPk7xHLvZWTHl9VKRO56sMUef/UrcUTqOswL0DSJtRIpZYZhclquQUfDxD6H2w8aWA=="
},
"react-popper": {
"version": "1.3.11",
"resolved": "https://registry.npmjs.org/react-popper/-/react-popper-1.3.11.tgz",
"integrity": "sha512-VSA/bS+pSndSF2fiasHK/PTEEAyOpX60+H5EPAjoArr8JGm+oihu4UbrqcEBpQibJxBVCpYyjAX7abJ+7DoYVg==",
"requires": {
"@babel/runtime": "^7.1.2",
"@hypnosphi/create-react-context": "^0.3.1",
"deep-equal": "^1.1.1",
"popper.js": "^1.14.4",
"prop-types": "^15.6.1",
"typed-styles": "^0.0.7",
"warning": "^4.0.2"
},
"dependencies": {
"popper.js": {
"version": "1.16.1",
"resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
"integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ=="
}
}
},
"react-refresh": {
"version": "0.8.3",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.8.3.tgz",
@ -14550,6 +14620,36 @@
"workbox-webpack-plugin": "5.1.4"
},
"dependencies": {
"@babel/core": {
"version": "7.12.3",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.3.tgz",
"integrity": "sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g==",
"requires": {
"@babel/code-frame": "^7.10.4",
"@babel/generator": "^7.12.1",
"@babel/helper-module-transforms": "^7.12.1",
"@babel/helpers": "^7.12.1",
"@babel/parser": "^7.12.3",
"@babel/template": "^7.10.4",
"@babel/traverse": "^7.12.1",
"@babel/types": "^7.12.1",
"convert-source-map": "^1.7.0",
"debug": "^4.1.0",
"gensync": "^1.0.0-beta.1",
"json5": "^2.1.2",
"lodash": "^4.17.19",
"resolve": "^1.3.2",
"semver": "^5.4.1",
"source-map": "^0.5.0"
},
"dependencies": {
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
}
}
},
"fs-extra": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
@ -14593,6 +14693,47 @@
"prop-types": "^15.6.2"
}
},
"reactstrap": {
"version": "8.9.0",
"resolved": "https://registry.npmjs.org/reactstrap/-/reactstrap-8.9.0.tgz",
"integrity": "sha512-pmf33YjpNZk1IfrjqpWCUMq9hk6GzSnMWBAofTBNIRJQB1zQ0Au2kzv3lPUAFsBYgWEuI9iYa/xKXHaboSiMkQ==",
"requires": {
"@babel/runtime": "^7.12.5",
"classnames": "^2.2.3",
"prop-types": "^15.5.8",
"react-popper": "^1.3.6",
"react-transition-group": "^2.3.1"
},
"dependencies": {
"@babel/runtime": {
"version": "7.13.10",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.10.tgz",
"integrity": "sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==",
"requires": {
"regenerator-runtime": "^0.13.4"
}
},
"dom-helpers": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz",
"integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==",
"requires": {
"@babel/runtime": "^7.1.2"
}
},
"react-transition-group": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz",
"integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==",
"requires": {
"dom-helpers": "^3.4.0",
"loose-envify": "^1.4.0",
"prop-types": "^15.6.2",
"react-lifecycles-compat": "^3.0.4"
}
}
}
},
"read-pkg": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
@ -16822,6 +16963,11 @@
"mime-types": "~2.1.24"
}
},
"typed-styles": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/typed-styles/-/typed-styles-0.0.7.tgz",
"integrity": "sha512-pzP0PWoZUhsECYjABgCGQlRGL1n7tOHsgwYv3oIiEpJwGhFTuty/YNeduxQYzXXa3Ge5BdT6sHYIQYpl4uJ+5Q=="
},
"typedarray": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
@ -16835,6 +16981,11 @@
"is-typedarray": "^1.0.0"
}
},
"typescript": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.3.tgz",
"integrity": "sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw=="
},
"unbox-primitive": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz",
@ -17173,6 +17324,14 @@
"makeerror": "1.0.x"
}
},
"warning": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
"integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
"requires": {
"loose-envify": "^1.0.0"
}
},
"watchpack": {
"version": "1.7.5",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz",

View file

@ -4,6 +4,7 @@
"private": true,
"version": "0.1.0",
"dependencies": {
"@babel/core": "^7.13.13",
"@material-ui/core": "^4.11.3",
"@material-ui/icons": "^4.11.2",
"@testing-library/jest-dom": "^4.2.4",
@ -13,8 +14,12 @@
"react-copy-to-clipboard": "^5.0.3",
"react-dom": "^16.14.0",
"react-dropzone": "^11.3.2",
"react-file-picker": "git+https://github.com/dantheuber/react-file-picker.git#master",
"react-ga": "^3.3.0",
"react-scripts": "^4.0.3"
"react-notifications-component": "^3.1.0",
"react-scripts": "^4.0.3",
"reactstrap": "^8.9.0",
"typescript": "^4.2.3"
},
"scripts": {
"predeploy": "npm run build",

View file

@ -17,14 +17,21 @@ import FileCopyOutlined from '@material-ui/icons/FileCopyOutlined';
import PublishOutlined from '@material-ui/icons/PublishOutlined';
// import { useFilePicker } from 'react-sage'
//import { FilePicker } from 'react-file-picker'
// import { FilePicker } from 'react-file-picker'
import { FilePicker } from '../src/Components'
import { CopyToClipboard } from 'react-copy-to-clipboard';
import ControlledAccordions from './ControlledAccordions'
import 'react-notifications-component/dist/theme.css'
import ReactNotification from 'react-notifications-component'
import { store } from 'react-notifications-component';
// import 'animate.css/animate.compat.css'
const useStyles = theme => ({
paper: {
@ -108,34 +115,51 @@ class ChecksumResolver extends React.Component {
return !this.isChecksumValid();
};
requestActivationBytes = () => {
addNotification = function (text, success = true) {
store.addNotification({
message: text,
type: success ? "success" : "danger",
// type: "danger",
insert: "bottom-left",
container: "top-full",
animationIn: ["animate__animated", "animate__fadeIn"],
animationOut: ["animate__animated", "animate__fadeOut"],
dismiss: {
duration: 3000,
onScreen: false
}
});
}
requestActivationBytes = async () => {
const { checksum } = this.state;
fetch("https://aax.api.j-kit.me/api/v2/activation/" + checksum)
.then(res => res.json())
.then(
(result) => {
const { success, activationBytes } = result;
if (success === true) {
this.setState({ activationBytes: result.activationBytes });
} else {
this.setState({ activationBytes: 'UNKNOWN' });
}
},
(error) => {
this.setState({ activationBytes: 'UNKNOWN' });
}
)
try {
let request = await fetch("https://aax.api.j-kit.me/api/v2/activation/" + checksum);
let result = await request.json();
const { success, activationBytes } = result;
if (success === true) {
this.setState({ activationBytes: activationBytes });
this.addNotification("Successfully resolved the activation bytes");
} else {
this.setState({ activationBytes: 'UNKNOWN' });
this.addNotification("An error occured while resolving the activation bytes, please check your inputs", false);
}
} catch (error) {
this.setState({ activationBytes: error });
this.addNotification("An error occured while resolving the activation bytes, please check your inputs", false);
}
}
buf2hex(buffer) { // buffer is an ArrayBuffer
return Array.prototype.map.call(new Uint8Array(buffer), x => ('00' + x.toString(16)).slice(-2)).join('');
}
acceptFile = async files => {
acceptFiles = async files => {
const file = files[0];
await this.acceptFile(file);
}
acceptFile = async file => {
// if (!file.name.toLowerCase().endsWith(".aax")) {
// alert('FileType not supported!');
// return;
@ -177,7 +201,7 @@ class ChecksumResolver extends React.Component {
noClick
onDrop={acceptedFiles => {
console.log(acceptedFiles);
this.acceptFile(acceptedFiles);
this.acceptFiles(acceptedFiles);
}}>
{({ getRootProps, getInputProps }) => (
<section>
@ -196,35 +220,19 @@ class ChecksumResolver extends React.Component {
autoFocus
onChange={(x) => this.setChecksum(x.target.value)}
value={checksum}
InputProps={{
readOnly: true,
// endAdornment: (
// // <IconButton onClick={() => {
// // alert('hi')
// // }}>
// // <PublishOutlined />
// // </IconButton>
// // <IconButton>
// // <HiddenFileInput accept=".jpg, .jpeg, .png" multiple={false} />
// // <PublishOutlined />
// // </IconButton>
// //accept="image/*"
// // <FilePicker
// // accept="image/*"
// // extensions={['aax','AAX']}
// // acceptFile="image/*"
// // onChange={FileObject => { }}
// // onError={errMsg => { }}
// // >
// // <IconButton >
// // <PublishOutlined />
// // </IconButton>
// // </FilePicker>
// )
readOnly: false,
endAdornment: (
<FilePicker
extensions={['aax', 'AAX']}
maxSize={99999}
onChange={this.acceptFile}
>
<IconButton >
<PublishOutlined />
</IconButton>
</FilePicker>
)
}}
/>
@ -280,6 +288,7 @@ class ChecksumResolver extends React.Component {
</Box>
</Container>
);
}

View file

@ -0,0 +1,45 @@
// external imports
import React from 'react'
import PropTypes from 'prop-types'
class FileInput extends React.Component {
constructor(props) {
super(props)
this._handleUpload = this._handleUpload.bind(this)
}
_handleUpload(evt) {
const file = evt.target.files[0]
this.props.onChange(file)
// free up the fileInput again
this.fileInput.value = null
}
render() {
return (
<div style={this.props.style}>
<input
accept={this.props.accept}
type="file"
style={{ display: 'none' }}
onChange={this._handleUpload}
ref={ele => (this.fileInput = ele)}
/>
{React.cloneElement(this.props.children, {
onClick: () => this.fileInput.click()
})}
</div>
)
}
}
FileInput.propTypes = {
style: PropTypes.object,
accept: PropTypes.string,
children: PropTypes.node.isRequired,
onChange: PropTypes.func.isRequired
}
export default FileInput

View file

@ -0,0 +1,80 @@
// external imports
import React from 'react'
import PropTypes from 'prop-types'
// local imports
import FileInput from '../FileInput'
class FilePicker extends React.Component {
constructor(props) {
super(props)
this._validate = this._validate.bind(this)
}
_validate(file) {
const { onError, onChange, maxSize, extensions } = this.props
// make sure a file was provided in the first place
if (!file) {
onError('Failed to upload a file.')
return
}
// if we care about file extensions
if (extensions) {
const uploadedFileExt = file.name
.split('.')
.pop()
.toLowerCase()
const isValidFileExt = extensions
.map(ext => ext.toLowerCase())
.includes(uploadedFileExt)
if (!isValidFileExt) {
onError(`Must upload a file of type: ${extensions.join(' or ')}`)
return
}
}
// convert maxSize from megabytes to bytes
const maxBytes = maxSize * 1000000
if (file.size > maxBytes) {
onError(`File size must be less than ${maxSize} MB.`)
return
}
// return native file object
onChange(file)
}
render() {
const { children, style } = this.props;
const accept = this.props.extensions.map(ext => `.${ext}`).join(',')
return (
<FileInput onChange={this._validate} style={style} accept={accept}>
{children}
</FileInput>
)
}
}
FilePicker.propTypes = {
children: PropTypes.node.isRequired,
onChange: PropTypes.func.isRequired,
onError: PropTypes.func.isRequired,
// max file size in MB
maxSize: PropTypes.number,
// file extension
extensions: PropTypes.array,
// validate file contents
validateContent: PropTypes.func,
style: PropTypes.object
}
FilePicker.defaultProps = {
maxSize: 2
}
export default FilePicker

View file

@ -0,0 +1,103 @@
// external imports
import React from 'react'
import { mount } from 'enzyme'
// local imports
import FilePicker from '.'
describe('File Picker', () => {
let onChange
let onError
beforeEach(() => {
onChange = jest.fn()
onError = jest.fn()
})
test('returns a valid component with required props', () => {
const ele = (
<FilePicker onChange={() => ({})} onError={() => ({})}>
<button>Click to upload</button>
</FilePicker>
)
expect(React.isValidElement(ele)).toBe(true)
})
test('call error handler when no file uploaded', () => {
// mount the select with a few options
const wrapper = mount(
<FilePicker onChange={onChange} onError={onError}>
<div>Click here</div>
</FilePicker>
)
// trigger the onChange callback on file input
wrapper.find('input').simulate('change', { target: { files: [] } })
expect(onError.mock.calls.length).toBe(1)
expect(onChange.mock.calls.length).toBe(0)
})
test('call error handler when a file with incorrect extension is uploaded', () => {
// mount the select with a few options
const wrapper = mount(
<FilePicker onChange={onChange} onError={onError} extensions={['md']}>
<div>Click here</div>
</FilePicker>
)
const file = new Blob(['file contents'], { type: 'text/plain' })
file.name = 'file.txt'
// trigger the onChange callback on file input
wrapper.find('input').simulate('change', { target: { files: [file] } })
expect(onError.mock.calls.length).toBe(1)
expect(onChange.mock.calls.length).toBe(0)
})
test('call error handler when a file that is too large is uploaded', () => {
// mount the select with a few options
const wrapper = mount(
<FilePicker
onChange={onChange}
onError={onError}
// set unreasonably small max size so that our tiny blob is too big
maxSize={0.0000000001}
>
<div>Click here</div>
</FilePicker>
)
const file = new Blob(['file contents'], { type: 'text/plain' })
// trigger the onChange callback on file input
wrapper.find('input').simulate('change', { target: { files: [file] } })
expect(onError.mock.calls.length).toBe(1)
expect(onChange.mock.calls.length).toBe(0)
})
test('call change handler when a file with correct size and extension is uploaded', () => {
// mount the select with a few options
const wrapper = mount(
<FilePicker
onChange={onChange}
onError={onError}
extensions={['txt']}
maxSize={1}
>
<div>Click here</div>
</FilePicker>
)
const file = new Blob(['file contents'], { type: 'text/plain' })
file.name = 'file.txt'
// trigger the onChange callback on file input
wrapper.find('input').simulate('change', { target: { files: [file] } })
expect(onError.mock.calls.length).toBe(0)
expect(onChange.mock.calls.length).toBe(1)
})
})

1
src/Components/index.js Normal file
View file

@ -0,0 +1 @@
export { default as FilePicker } from './FilePicker'

View file

@ -0,0 +1,2 @@
export { default as loadImage } from './load-image'
export { default as loadFile } from './load-file'

View file

@ -0,0 +1,11 @@
export default function loadFile(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.readAsDataURL(file)
reader.onloadend = loadedFIle => resolve(loadedFIle.target.result)
reader.onerror = () => reject(new Error('There was an error uploading the file'))
})
}

View file

@ -0,0 +1,35 @@
export default function loadImg(dataUrl, dims) {
// destructure props from dims
const { minWidth, maxWidth, minHeight, maxHeight } = dims
return new Promise((resolve, reject) => {
// create a new html image element
const img = new Image()
// set the image src attribute to our dataUrl
img.src = dataUrl
// listen for onload event
img.onload = () => {
// validate the min and max image dimensions
if (img.width < minWidth || img.height < minHeight) {
reject(
new Error(
`The uploaded image is too small. Must be at least ${minWidth}px by ${minHeight}px.`
)
)
}
if (img.width > maxWidth || img.height > maxHeight) {
reject(
new Error(
`The uploaded image is too large. Must be no more than ${maxWidth}px by ${maxHeight}px.`
)
)
}
resolve(true)
}
img.onerror = () => reject(new Error('There was an error uploading the image'))
})
}

View file

@ -3,13 +3,21 @@ import ReactDOM from 'react-dom';
import './index.css';
import * as serviceWorker from './serviceWorker';
import ChecksumResolver from './ChecksumResolver';
import ReactNotification from 'react-notifications-component'
import ReactGA from 'react-ga';
ReactGA.initialize('UA-174657678-1');
ReactGA.pageview(window.location.pathname + window.location.search);
ReactDOM.render(
<ChecksumResolver />,
<div>
<div style={{display:'flex'}}>
<ReactNotification />
</div>
<ChecksumResolver />
</div>,
document.getElementById('root')
);