-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
184 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import React from 'react'; | ||
import { useMutation } from '@tanstack/react-query'; | ||
import Cookies from 'js-cookie'; | ||
|
||
import { Attribute } from '../lib/SimpleForm/types'; | ||
import { useNileConfig } from '../context'; | ||
import SimpleForm from '../lib/SimpleForm'; | ||
import { AttributeType } from '../lib/SimpleForm/types'; | ||
|
||
import { Props, AllowedAny } from './types'; | ||
|
||
export default function LoginForm(props: Props) { | ||
const { workspace, database, basePath, allowClientCookies } = useNileConfig(); | ||
|
||
const { attributes, onSuccess, onError, beforeMutate } = props; | ||
const fetchPath = `${basePath}/workspaces/${workspace}/databases/${database}/users/login`; | ||
|
||
const handleMutate = | ||
typeof beforeMutate === 'function' | ||
? beforeMutate | ||
: (data: AllowedAny): AllowedAny => data; | ||
|
||
const mutation = useMutation( | ||
async (data: { email: string; password: string }) => { | ||
const _data = handleMutate(data); | ||
const res = await fetch(fetchPath, { | ||
method: 'POST', | ||
body: JSON.stringify(_data), | ||
headers: { | ||
'content-type': 'application/json', | ||
}, | ||
}).catch((e) => e); | ||
if (res.ok === false) { | ||
throw new Error(res.status); | ||
} | ||
try { | ||
return await res.json(); | ||
} catch (e) { | ||
return e; | ||
} | ||
}, | ||
{ | ||
onSuccess: (token, data) => { | ||
if (token) { | ||
if (allowClientCookies) { | ||
Cookies.set('token', token.token, { | ||
'max-age': token.maxAge, | ||
}); | ||
} | ||
|
||
onSuccess && onSuccess(token, data); | ||
} | ||
}, | ||
onError: (error, data) => { | ||
onError && onError(error as Error, data); | ||
}, | ||
} | ||
); | ||
|
||
const completeAttributes = React.useMemo(() => { | ||
const mainAttributes: Attribute[] = [ | ||
{ | ||
name: 'email', | ||
label: 'Email', | ||
type: AttributeType.Text, | ||
defaultValue: '', | ||
required: true, | ||
}, | ||
{ | ||
name: 'password', | ||
label: 'Password', | ||
type: AttributeType.Password, | ||
defaultValue: '', | ||
required: true, | ||
}, | ||
]; | ||
if (attributes && attributes.length > 0) { | ||
return mainAttributes.concat(attributes); | ||
} | ||
return mainAttributes; | ||
}, [attributes]); | ||
|
||
return ( | ||
<SimpleForm | ||
mutation={mutation} | ||
buttonText="Log in" | ||
attributes={completeAttributes} | ||
/> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { default } from './LoginForm'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { Token } from '@theniledev/js'; | ||
|
||
import { Attribute } from '../lib/SimpleForm/types'; | ||
|
||
type LoginSuccess = ( | ||
token: Token, | ||
LoginInfo: { email: string; password: string } | ||
) => void; | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
export type AllowedAny = any; | ||
|
||
export interface Props { | ||
beforeMutate?: (data: AllowedAny) => AllowedAny; | ||
onSuccess: LoginSuccess; | ||
onError?: (error: Error, data: AllowedAny) => void; | ||
attributes?: Attribute[]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
24 changes: 24 additions & 0 deletions
24
packages/react/stories/LoginForm/UserLoginForm.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import React from 'react'; | ||
import { Meta, Story } from '@storybook/react'; | ||
import Stack from '@mui/joy/Stack'; | ||
|
||
import LoginForm from '../../src/LoginForm'; | ||
import { NileProvider } from '../../src/context'; | ||
|
||
const meta: Meta = { | ||
component: LoginForm, | ||
}; | ||
|
||
export default meta; | ||
|
||
const Template: Story<null> = () => ( | ||
<NileProvider database="database" workspace="workspace"> | ||
<Stack sx={{ maxWidth: '20rem', margin: '0 auto' }} spacing={2}> | ||
<LoginForm onSuccess={() => alert('success!')} /> | ||
</Stack> | ||
</NileProvider> | ||
); | ||
|
||
// By passing using the Args format for exported stories, you can control the props for a component for reuse in a test | ||
// https://storybook.js.org/docs/react/workflows/unit-testing | ||
export const Default = Template.bind({}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import React from 'react'; | ||
import { render, screen, fireEvent, waitFor } from '@testing-library/react'; | ||
import { NileProvider } from '@theniledev/react'; | ||
import Cookies from 'js-cookie'; | ||
import '../matchMedia.mock'; | ||
|
||
import LoginForm from '../../src/LoginForm/LoginForm'; | ||
import { token } from '../fetch.mock'; | ||
|
||
jest.mock('js-cookie'); | ||
|
||
describe('LoginForm', () => { | ||
it('sets a js cookie by default', async () => { | ||
const spy = jest.spyOn(Cookies, 'set'); | ||
const onSuccess = jest.fn(); | ||
global.fetch = token; | ||
render( | ||
<NileProvider workspace="workspace" database="database"> | ||
<LoginForm onSuccess={onSuccess} /> | ||
</NileProvider> | ||
); | ||
const password = screen.getByPlaceholderText('Password'); | ||
fireEvent.change(password, { target: { value: 'supersecret' } }); | ||
|
||
const email = screen.getByPlaceholderText('Email'); | ||
fireEvent.change(email, { target: { value: '[email protected]' } }); | ||
|
||
const button = screen.getByRole('button', { name: 'Log in' }); | ||
fireEvent.click(button); | ||
|
||
await waitFor(() => expect(onSuccess).toHaveBeenCalledTimes(1)); | ||
expect(spy).toHaveBeenCalledTimes(1); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
399e910
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
react-storybook – ./
react-storybook-ten.vercel.app
react-storybook-theniledev.vercel.app
react-storybook-git-master-theniledev.vercel.app
storybook.thenile.dev