Vue test login form - javascript

I created a login form with vuejs. I'm new with the vue framework and want to write a test for my api call.
This is the form:
<template>
<div>
<div class="col q-mb-md">
<div class="row">
<q-btn
#click="loginGoogle"
type="submit"
color="primary"
class="col"
name="login_google"
outline
size="lg"
label="Login with Google" />
</div>
</div>
<div class="col q-mb-md">
<div class="row">
<q-btn
type="submit"
color="primary"
class="col"
name="login_apple"
outline
size="lg"
label="Login with Apple ID" />
</div>
</div>
<form #submit.prevent="submitForm" class="q-mt-lg">
<div class="col q-mb-md">
<q-input
v-model="formData.email"
outlined
class="col"
label="Email"
ref="email"
stack-label
:rules="[ val => validateEmail(val) || 'Please enter a valid email address']"
lazy-rules />
</div>
<div class="col q-mb-md">
<q-input
v-model="formData.password"
type="password"
outlined
class="col"
label="Password"
ref="password"
stack-label
:rules="[ val => val.length >= 8 || 'Password must be at least 8 characters']"
lazy-rules />
</div>
<div class="row">
<q-btn
type="submit"
name="login"
class="login"
color="primary"
label="Login" />
</div>
</form>
</div>
</template>
Action method with api call:
submitForm () {
this.$refs.email.validate()
this.$refs.password.validate()
if (!this.$refs.email.hasError && !this.$refs.password.hasError) {
authService.login({ email: this.formData.email.toLowerCase(), password: this.formData.password })
.then((res) => {
this.$store.commit('SET_AUTH_USER')
localStorage.setItem('access_token', res.access_token)
localStorage.setItem('refresh_token', res.refresh_token)
this.$store.dispatch('GET_ME').then((me) => {
this.$router.push({ path: '/' })
})
}).catch((err) => {
if (err.status === 500) {
this.$swal({ icon: 'error', title: 'Something went wrong!' })
} else {
this.$swal({ icon: 'error', title: 'Wrong data!' })
}
})
}
}
My api call:
/**
* Send request for login user and set token in localstorage
*
* #param {String} email
* #param {String} password
* #returns {Object}
*/
async function login (data) {
const requestOptions = {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Accept: 'application/json'
},
body: `email=${data.email.trim()}&password=${data.password.trim()}`
}
const res = await fetch('http://localhost/api/login', requestOptions)
if ((await res.status) !== 200) {
// eslint-disable-next-line no-throw-literal
throw { status: res.status, message: res.statusText }
} else {
return await res.json()
}
}
I read that I have to mock the action from vuex store and to trigger the action. So my test looks like this:
import Vuex from 'vuex'
import { mount, createLocalVue } from '#vue/test-utils'
import LoginComponent from '../components/Auth/Login'
const localVue = createLocalVue()
localVue.use(Vuex)
describe('Login form', () => {
it('calls the login action correctly', () => {
const loginMock = jest.fn(() => Promise.resolve())
const store = new Vuex.Store({
actions: {
// mock function
submitForm: loginMock
}
})
const wrapper = mount(LoginComponent, { localVue, store })
wrapper.find('form').trigger('submit.prevent')
expect(loginMock).toHaveBeenCalled()
})
})
The test is failed:
Login form
✕ calls the login action correctly (50 ms)
● Login form › calls the login action correctly
expect(jest.fn()).toHaveBeenCalled()
Expected number of calls: >= 1
Received number of calls: 0
18 | const wrapper = mount(LoginComponent, { localVue, store })
19 | wrapper.find('form').trigger('submit.prevent')
> 20 | expect(loginMock).toHaveBeenCalled()
| ^
21 | })
22 | })
23 |
at Object.<anonymous> (src/specs/login.spec.js:20:23)
Can you give me a suggestions what are the tests (not only the for api call) I need to write to make sure my form works correctly? Thank you!

You have to await the trigger since it is making an asyn function call
await wrapper.find('form').trigger('submit.prevent')
then
wrapper.$nextTick(() => {
expect(loginMock).toHaveBeenCalled()
})

amend the last bit. You omitted vm.
wrapper.vm.$nextTick(() => { expect(loginMock).toHaveBeenCalled(); });

Related

creating custom error when no entries has been made using toast for React

I am in the middle of creating error handlings using toast function for my react app signin and register pages, I was able to successfully create a toast function that when logging in with wrong email or password they should receive an error message.
however, I am trying to accomplish the same thing with a different error message when the user clicks on sign in without entering any information, but I can't get it to work, when I click on the login without entering anything it gives me the same error messgae as i set up when entering wrong credentials.
what am I missing?
please help me
from signin.js
import React from "react";
import { ToastContainer, toast } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
class Signin extends React.Component {
constructor(props) {
super(props);
this.state = {
signInEmail: "",
signInPassword: "",
};
}
showToast = () => {
toast.error("invalid username or password", {
position: "top-right",
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
});
};
showToast1 = () => {
toast.error("invalid username or password", {
position: "top-right",
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
});
};
onEmailChange = (event) => {
this.setState({ signInEmail: event.target.value });
};
onPasswordChange = (event) => {
this.setState({ signInPassword: event.target.value });
};
onSubmitSignIn = (event) => {
event.preventDefault();
let regex = /^[A-Za-z0-9_!#$%&'*+\/=?`{|}~^.-]+#[A-Za-z0-9.-]+$/g;
const isEmailValid = this.state.signInEmail.match(regex);
const signInEmail = this.state.signInEmail
const signInPassword = this.state.signInPassword
if (!isEmailValid) {
return this.showToast();
} else if(!signInEmail || !signInPassword) {
return this.showToast1()
}
fetch("https://ancient-sea-46547.herokuapp.com/signin", {
method: "post",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
email: this.state.signInEmail,
password: this.state.signInPassword,
}),
})
.then((response) => {
if (!response.ok) throw new Error("invalid");
return response.json();
})
.then((user) => {
if (user.id) {
this.props.loadUser(user);
this.props.onRouteChange("home");
}
})
.catch((error) => this.showToast(), this.showToast1);
};
render() {
const { onRouteChange } = this.props;
return (
<article className="br3 ba b--black-10 mv4 w-100 w-50-m w-25-l mw6 shadow-5 center">
<ToastContainer />
<main className="pa4 black-80">
<form className="measure">
<fieldset id="sign_up" className="ba b--transparent ph0 mh0">
<legend className="f1 fw6 ph0 mh0">Sign In</legend>
<div className="mt3">
<label className="db fw6 lh-copy f6" htmlFor="email-address">
Email
</label>
<input
className="pa2 input-reset ba bg-transparent hover-bg-black hover-white w-100"
type="email"
name="email-address"
id="email-address"
onChange={this.onEmailChange}
/>
</div>
<div className="mv3">
<label className="db fw6 lh-copy f6" htmlFor="password">
Password
</label>
<input
className="b pa2 input-reset ba bg-transparent hover-bg-black hover-white w-100"
type="password"
name="password"
id="password"
onChange={this.onPasswordChange}
/>
</div>
</fieldset>
<div className="">
<input
onClick={this.onSubmitSignIn}
className="b ph3 pv2 input-reset ba b--black bg-transparent grow pointer f6 dib"
type="submit"
value="Sign in"
/>
</div>
<div className="lh-copy mt3">
<p
onClick={() => onRouteChange("register")}
className="f6 link dim black db pointer"
>
Register
</p>
</div>
</form>
</main>
</article>
);
}
}
export default Signin;

React Hooks throws error Hooks can only be called inside of the body of a function component.. after upgrade to web pack 4

I have Rails Application with React On Rails, recently upgraded webpack 3 to webpack 4
Rails : 4
Ruby: 2+
React: 16.9
Webpack: 4.41.5
everything works well except below code which works well with webpack 3
In the console I see below error
Uncaught Invariant Violation: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
An error occurred during a React Lifecycle function
import Error from '#material-ui/icons/Error';
import MoreHoriz from '#material-ui/icons/MoreHoriz';
import { Button, FixedFooterModal, SafeAnchor, Util } from 'lib-components';
import moment from 'moment-timezone';
import prettyBytes from 'pretty-bytes';
import React, { useState, useCallback, useMemo, useEffect } from 'react';
import Helpers from '../../../lib/lib-components/src/js/services/Helpers'; // eslint-disable-line
import styles from './ExportForm.css';
interface Export {
created_at: string;
filename: string;
filesize: number;
id: number;
project_id: number;
status: string;
updated_at: string;
options: string;
}
function parseOptions(options: string) {
return options
.split(',')
.map(option => {
switch (option) {
case 'include_done_stories':
return 'Done';
case 'include_current_backlog_stories':
return 'Current/Backlog';
case 'include_icebox_stories':
return 'Icebox';
case 'include_epics':
return 'Epics';
case 'include_attachments':
return 'Attachments';
case 'include_project_history':
return 'Project History';
}
})
.join(', ');
}
const ExportStatus: React.FunctionComponent<{
status: string;
projectId: number;
exportId: number;
csrfToken: string;
setModalOpen: (a: boolean) => void;
}> = ({ status, projectId, exportId, csrfToken, setModalOpen }) => {
const handleRetryClick = useCallback(() => {
Helpers.update(csrfToken, {
method: 'GET',
url: `/projects/${projectId}/export/${exportId}/retry`,
});
setModalOpen(true);
}, [projectId, exportId, csrfToken, setModalOpen]);
switch (status) {
case 'failed':
return (
<div data-aid='ExportForm__failed' className={styles['ExportForm__failed']}>
<div className={styles['ExportForm__failedIcon']}>
<Error fontSize='small' style={{ marginRight: '4px', color: '#CB2B1E' }} />
<span>Export failed</span>
</div>
<div data-aid='ExportForm__retryLink' className={styles['ExportForm__retryLink']}>
<SafeAnchor onClick={handleRetryClick}>retry</SafeAnchor>
</div>
</div>
);
case 'completed':
return (
<div data-aid='ExportForm__completed' className={styles['ExportForm__completed']}>
<div className={styles['ExportForm__completedIcon']}>
<CheckCircle fontSize='small' style={{ marginRight: '4px', color: '#6D902A' }} />
<span>Complete</span>
</div>
<div className={styles['ExportForm__completedDownloadLink']}>
<SafeAnchor href={`/projects/${projectId}/export/${exportId}/download`}>download</SafeAnchor>
</div>
</div>
);
default:
case 'in progress':
return (
<div className={styles['ExportForm__inProgressIcon']}>
<div className={styles['ExportForm__inProgressIconBg']}>
<MoreHoriz fontSize='small' />
</div>
<span data-aid='ExportForm__inProgress' className={styles['ExportForm__inProgress']}>
In Progress
</span>
</div>
);
}
};
const ExportForm: React.FunctionComponent<{
csrfToken: string;
id: number;
exports: Export[];
timezone: string;
}> = ({ csrfToken, id, exports, timezone }) => {
const [modalOpen, setModalOpen] = useState(false);
const [includeDoneStories, setIncludeDoneStories] = useState(true);
const [includeCurrentBacklogStories, setIncludeCurrentBacklogStories] = useState(true);
const [includeIceboxStories, setIncludeIceboxStories] = useState(true);
const [includeEpics, setIncludeEpics] = useState(true);
const [includeAttachments, setIncludeAttachments] = useState(true);
const [includeProjectHistory, setIncludeProjectHistory] = useState(true);
const setDoneStoriesFromEvent = useCallback(e => setIncludeDoneStories(e.target.checked), []);
const setCurrentBacklogStoriesFromEvent = useCallback(e => setIncludeCurrentBacklogStories(e.target.checked), []);
const setIceboxStoriesFromEvent = useCallback(e => setIncludeIceboxStories(e.target.checked), []);
const setEpicsFromEvent = useCallback(e => setIncludeEpics(e.target.checked), []);
const setAttachmentsFromEvent = useCallback(e => setIncludeAttachments(e.target.checked), []);
const setProjectHistoryFromEvent = useCallback(e => setIncludeProjectHistory(e.target.checked), []);
const handleExportClicked = useCallback(() => {
Helpers.update(
csrfToken,
{
method: 'POST',
url: `/projects/${id}/export`,
},
{
options: {
include_done_stories: includeDoneStories,
include_current_backlog_stories: includeCurrentBacklogStories,
include_icebox_stories: includeIceboxStories,
include_epics: includeEpics,
include_attachments: includeAttachments,
include_project_history: includeProjectHistory,
},
}
);
setModalOpen(true);
}, [
csrfToken,
id,
includeDoneStories,
includeCurrentBacklogStories,
includeIceboxStories,
includeEpics,
includeAttachments,
includeProjectHistory,
]);
const handleCloseModal = useCallback(() => {
setModalOpen(false);
Util.windowLocation().assign(`/projects/${id}/export`);
}, [id]);
const justRefresh = useMemo(() => exports.some(e => e.status === 'in progress'), [exports]);
useEffect(() => {
let timer: ReturnType<typeof setTimeout>;
if (justRefresh) {
timer = setTimeout(() => Util.windowLocation().reload(), 30000);
}
return () => {
clearTimeout(timer);
};
}, [justRefresh]);
return (
<div className={styles['ExportForm']}>
<h2>Create New Export</h2>
<p>
Stories, Epics, and Project History will be exported as a CSV. All files will be available to download from the
exports section below. Files are available for two weeks.
</p>
<div className={styles['ExportForm__options']}>
<div className={styles['ExportForm__option']}>
<label>
<input type='checkbox' checked={includeDoneStories} onChange={setDoneStoriesFromEvent} />
All Done Stories
</label>
</div>
<div className={styles['ExportForm__option']}>
<label>
<input
type='checkbox'
checked={includeCurrentBacklogStories}
onChange={setCurrentBacklogStoriesFromEvent}
/>
All Current/Backlog Stories
</label>
</div>
<div className={styles['ExportForm__option']}>
<label>
<input type='checkbox' checked={includeIceboxStories} onChange={setIceboxStoriesFromEvent} />
All Icebox Stories
</label>
</div>
<div className={styles['ExportForm__option']}>
<label>
<input type='checkbox' checked={includeEpics} onChange={setEpicsFromEvent} />
All Epics
</label>
</div>
<div className={styles['ExportForm__option']}>
<label>
<input
data-aid='ExportForm__attachments'
type='checkbox'
checked={includeAttachments}
onChange={setAttachmentsFromEvent}
/>
All Attachments
</label>
</div>
<div className={styles['ExportForm__option']}>
<label>
<input
data-aid='ExportForm__attachments'
type='checkbox'
checked={includeProjectHistory}
onChange={setProjectHistoryFromEvent}
/>
Project History
</label>
</div>
</div>
<Button
label={<span>Export</span>}
size='large'
type='primary'
data-aid='ExportForm__exportButton'
onClick={handleExportClicked}
/>
{modalOpen && (
<FixedFooterModal
buttons={[
{
props: {
label: 'Close',
type: 'lined',
onClick: handleCloseModal,
align: 'right',
'data-aid': 'ExportForm__closeModal',
},
},
]}
title='Export in progress'
onClose={handleCloseModal}
noScroll={true}
>
<div className={styles['ExportForm__inProgressModal']}>
<p>We're on it! We will send you an email once your data is available to download.</p>
<p>
Check back here to find all of your available exports. You can access your exported data here for two
weeks.
</p>
</div>
</FixedFooterModal>
)}
<h2>Exports</h2>
{exports.length > 0 ? (
exports.map(exp => (
<div key={exp.id} className={styles['ExportForm__export']} data-aid='ExportForm__export'>
<div className={styles['ExportForm__exportInfo']}>
<div className={styles['ExportForm__exportTimes']}>
<span data-aid='ExportForm__created'>
{moment.tz(exp.created_at, timezone).format('MMM Do, YYYY [at] h:mm A z')}
</span>
{exp.filesize ? ` (${prettyBytes(exp.filesize)}) • ` : ` • `}
<span data-aid='ExportForm__expires'>
Expires on {moment.tz(exp.created_at, timezone).add(14, 'days').format('MMM Do, YYYY')}
</span>
</div>
<div className={styles['ExportForm__exportOptions']}>
<span data-aid='ExportForm__exportOptions'>({parseOptions(exp.options)})</span>
</div>
</div>
<div className={styles['ExportForm__download']}>
<ExportStatus
status={exp.status}
projectId={exp.project_id}
exportId={exp.id}
csrfToken={csrfToken}
setModalOpen={setModalOpen}
/>
</div>
</div>
))
) : (
<div className={styles['ExportForm__emptyMessage']} data-aid='ExportForm__emptyMessage'>
<p>No exports have been created.</p>
</div>
)}
</div>
);
};
export default ExportForm;
I tried following solutions for similar error but of no help
Checked for multiple versions of react
disabling react hot loader and setConfig pureSFC to true
Used React.FunctionComponent
Also this componenet is rendered by React.lazy() later I figured all the components rendered by lazy loading have inavlid hook call

VueJs3 sending data from one component to another

I am currently working on a VueJs3 Project. I already coded a login system, which works perfectly fine. I now want to have a div on the homepage, that tells the user with which account they are logged in.
"You are logged in as:" + username
I therefore, have to send the username from the login component to the home component. Can someone help me out here? I have tried out a lot of ways but none of them seem to work.
Btw. the components are all saved in the components folder
Here is my code:
home.vue
<div class="home">
<h1>You are logged in as: {{currentusername}}</h1>
<Maschinen/>
</div>
</template>
<script>
import Maschinen from '#/components/Maschinen.vue'
import axios from 'axios'
export default {
name: 'Home',
components: {
Maschinen
},
data() {
return {
currentusername: null,
}
},
async created() {
const response = await axios.get('http://localhost:8080/user/hetwinuser', {
headers: {
Authorization: 'Bearer ' + localStorage.getItem('token')
}
});
this.currentusername= (response.data.username);
}
}
</script>
login.vue:
<template>
<form #submit.prevent="handleSubmit">
<h1>Login</h1>
<!--username + pw input for login-->
<div class="form-group">
<label id="username">Username:</label>
<input id="usernameinput" type="text" class="form-control" v-model="username" placeholder="Username">
</div>
<div>
<label id="password">Password:</label>
<input id="passwordinput" type="password" class="form-control" v-model="password" placeholder="Password">
</div>
<!--enter buttons-->
<button class="enter">Enter</button>
<router-link to="/register" id="goToRegister" tag="button">Register</router-link>
</form>
</template>
<script>
import axios from 'axios';
export default {
name: "Login",
data: () => {
return {
username: '',
password: ''
}
},
methods: {
async handleSubmit() {//executed when enter button is pressed
const data = {
username: this.username,
password: this.password
};
axios.post('http://localhost:8080/authenticate', data) //post the username + pw and will receive the token in exchange
.then(
res => {
localStorage.setItem('token', res.data.jwt);
localStorage.setItem('currentusername', this.username);
//console.log(localStorage.getItem('token'));
}
).catch(
err => {
console.log(err)
}
);
}
},
}
</script>```
username and password
Define it under store -> state.js. Then where you want it
Use it as "this.$store.state.username"

REACTJS AND API pass the value of Api to another page

I really need help cause I'm totally new to reactjs. How can I maintain the logged-in user throughout the page? like a session. the code shows the data called when I logged in. I'm really lost here in react.
When the credentials are true, here's the result
{userId: 1, firstname: "Jeff", middlename: null, lastname: "Geli", positionId: 1, …}
userId: 1
firstname: "Jeff"
middlename: null
lastname: "Geli"
positionId: 1
token: "eyJhbGciOiJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNobWFjLXNoYTI1NiIsInR5cCI6IkpXVCJ9.eyJMb2dnZWRVc2VyIjoie1wiVXNlcklkXCI6MSxcIkZpcnN0bmFtZVwiOlwiSmVmZlwiLFwiTWlkZGxlbmFtZVwiOm51bGwsXCJMYXN0bmFtZVwiOlwiR2VsaVwiLFwiUG9zaXRpb25JZFwiOjEsXCJUb2tlblwiOm51bGx9IiwiZXhwIjoxNTc5NzU5MzI5LCJpc3MiOiJzbWVzay5pbiIsImF1ZCI6InJlYWRlcnMifQ.xXPW0ijBdULuMBfhFGyL1qF1bA--FzG64jEJVMQZWY8"
__proto__: Object
import React from 'react';
import { Link, withRouter } from 'react-router-dom';
import { PageSettings } from './../../config/page-settings.js';
import bg from '../../assets/login-bg-17.jpg';
import axios from "axios";
class LoginV2 extends React.Component {
static contextType = PageSettings;
constructor(props) {
super(props);
//credentials for login
this.state = {
username: '',
password: '',
};
this.handleSuccess = this.handleSuccess.bind(this);
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
componentDidMount() {
this.context.handleSetPageSidebar(false);
this.context.handleSetPageHeader(false);
}
componentWillUnmount() {
this.context.handleSetPageSidebar(true);
this.context.handleSetPageHeader(true);
}
handleChange(event) {
this.setState({
[event.target.name]: event.target.value
});
};
handleSuccess(data) {
alert("Logged IN");
this.props.history.push('dashboard/v2');
}
//When submit button is clicked, fetch API
async handleSubmit(event) {
event.preventDefault();
//fetch API, method POST
const getCred = await fetch('/api/login', {
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'ApiKey': "Secret"
},
method: "POST",
body: JSON.stringify({
username: this.state.username,
password: this.state.password
}),
});
const data = await getCred.json();
if (getCred.status == 400) {
alert(data);
} else {
this.handleSuccess(getCred);
}
console.log(data);
}
render() {
return (
<React.Fragment>
<div className="login-cover">
<div className="login-cover-image" style={{backgroundImage:'url('+ bg +')'}} >
</div>
<div className="login-cover-bg"></div>
</div>
<div className="login login-v2">
<div className="login-header">
<div className="brand">
<span className="logo"></span> <b>Tek</b> Teach
<small>Leraning Management System</small>
</div>
<div className="icon">
<i className="fa fa-lock"></i>
</div>
</div>
<div className="login-content">
<form className="margin-bottom-0" onSubmit={this.handleSubmit}>
<div className="form-group m-b-20">
<input type="text" className="form-control form-control-lg" placeholder="Username" name="username" value={this.state.username} onChange={this.handleChange} required />
</div>
<div className="form-group m-b-20">
<input type="password" className="form-control form-control-lg" placeholder="Password" name="password" value={this.state.password} onChange={this.handleChange} required />
</div>
<div className="login-buttons">
<button type="submit" className="btn btn-success btn-block btn-lg" onClick={this.login}>Sign me in</button>
</div>
</form>
</div>
</div>
</React.Fragment>
)
}
}
export default withRouter(LoginV2);
You could use sessionStorage or localStorage to store the token and then check for it, if it is set then keep user logged in otherwise logout user.
set the session once user logged in
if (token) {
localStorage.setItem('session', JSON.stringify(token));
}
To check if the user is logged in
let user = localStorage.getItem('session');
if (user) {
console.log('Valid session');
} else {
console.log('Not valid session');
}
On logut clear the value
let user = localStorage.getItem('session');
if (user) {
localStorage.removeItem();
}
Adding the logic in the provided code
async handleSubmit(event) {
// .... Other code
const data = await getCred.json();
if (data.status == 400) {
alert(data);
} else {
this.handleSuccess(data);
}
console.log(data);
}
//Set your token in handleSuccess method
handleSuccess(data) {
alert("Logged IN");
if (data.token) {
localStorage.setItem('session', JSON.stringify(data.token));
}
this.props.history.push('dashboard/v2');
}

React Formik file upload with laravel back-end

I'am using Formik for my forms in React. Everything is okay but for the file input.
The user must be able to upload a CV. The {values.cv} in Formik does indicate that there is a value in the input. However when I send it to my Laravel back-end, I get an empty array.
I want to be able to store the file in my database.
Any help would be appreciated.
The Formik file input:
<label className="btn btn-info" htmlFor="cv">
<input id="cv" name="cv" style={{display: 'none'}} type="file" onChange={(event) => {
setFieldValue("cv", event.currentTarget.files[0]);
}} />
</label>
The cv input default value is : ' '
This is what is in the {values.cv}
Here is the console.log of it
In my Laravel back-end I return the $request and React is logging it.
This is what I see then.
cv : []
Here is a picture of my console
As per this post.
<input id="file" name="file" type="file" onChange={(event) => {
setFieldValue("file", event.currentTarget.files[0]);
}} />
You can then access all the stuff...
JSON.stringify({
fileName: values.file.name,
type: values.file.type,
size: `${values.file.size} bytes`
}, null, 2);
Formik doesn't handle file upload, but there's a hack around it provided here https://github.com/jaredpalmer/formik/issues/45
import * as React from 'react';
import { AxiosRequestConfig } from 'axios';
import Image from 'components/Image';
import { Progress } from 'components/Progress';
import ToasterInstance from '../Toast/ToasterInstance';
import { axios } from 'api/axios.config';
import { toApiError } from 'utils/api';
export interface MediaUploadProps {
id: string;
slug: string;
value: string;
onChange: (field: string, mediaId: string) => void;
}
export interface MediaUploadState {
progress: number;
file?: File;
error?: string;
}
export class MediaUpload extends React.Component<
MediaUploadProps,
MediaUploadState
> {
state: MediaUploadState = { progress: -1 };
handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
if (!e.target.files) {
return;
}
let file = e.target.files[0];
this.setState({ file: file });
let data = new FormData();
data.append('file', file);
let config: AxiosRequestConfig = {
onUploadProgress: (p: any) => {
this.setState({ progress: Math.round(p.loaded * 100 / p.total) });
},
};
this.setState({ error: undefined, progress: 0 });
axios.post('/v1/media?slug=' + this.props.slug, data, config).then(
res => {
this.setState({ error: undefined, progress: -1 });
this.props.onChange(this.props.id, res.data.path);
},
err => {
const message = toApiError(err);
this.setState({ error: message, progress: -1 });
ToasterInstance.show({
message,
iconName: 'danger',
intent: 'danger',
});
}
);
}
handleRemoveImage = () => {
this.props.onChange(this.props.id, '');
}
render() {
return (
<div>
<div>
{this.props.value !== '' &&
this.state.progress === -1 &&
<Image path={this.props.value} size="lg" />}
<div style={{ maxWidth: 144 }}>
{this.state.progress > -1 &&
<Progress percentage={this.state.progress} />}
</div>
{this.props.value &&
<a
style={{ marginTop: -40 }}
className="button button--negative button--small button--secondary"
role="button"
onClick={this.handleRemoveImage}
>
Remove
</a>}
</div>
<div style={{ marginTop: 10 }}>
<label className="button button--purple button--secondary">
Upload new picture
<input
className="visually-hidden"
type="file"
onChange={this.handleFileChange}
/>
</label>
</div>
</div>
);
}
}

Categories