Skip to content

Commit b28fb0b

Browse files
authored
Merge pull request #27 from arch-spatula/feat/login-page
Feat/login page
2 parents 0f7182d + 63fd226 commit b28fb0b

File tree

20 files changed

+317
-76
lines changed

20 files changed

+317
-76
lines changed

src/App.test.tsx

Lines changed: 0 additions & 18 deletions
This file was deleted.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import styled from '@emotion/styled';
2+
3+
export const ButtonWrapper = styled.button`
4+
${(props) => props.theme.fonts.body16Regular}
5+
border-radius: 0.25rem;
6+
border: none;
7+
background-color: ${(props) =>
8+
props.disabled ? props.theme.colors.gray400 : props.theme.colors.green};
9+
padding: 0.25rem 0.75rem;
10+
color: ${(props) => props.theme.colors.white};
11+
`;
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { render, screen } from '../../../libs/test-utils';
2+
import { describe, vi, it } from 'vitest';
3+
import { Button } from '.';
4+
import user from '@testing-library/user-event';
5+
6+
describe('Button', () => {
7+
it('should invoke the function when the button is clicked', async () => {
8+
user.setup();
9+
const btnText = 'Button';
10+
const mock = vi.fn(() => 0);
11+
12+
render(<Button onClick={mock}>{btnText}</Button>);
13+
const btnElement = screen.getByRole('button');
14+
await user.click(btnElement);
15+
16+
expect(btnElement).toBeInTheDocument();
17+
expect(mock).toHaveBeenCalledTimes(1);
18+
});
19+
20+
it('should not invoke the function when the button is disabled', async () => {
21+
user.setup();
22+
const btnText = 'Button';
23+
const mock = vi.fn(() => 0);
24+
25+
render(
26+
<Button onClick={mock} disabled>
27+
{btnText}
28+
</Button>
29+
);
30+
const btnElement = screen.getByRole('button');
31+
await user.click(btnElement);
32+
33+
expect(btnElement).toBeDisabled();
34+
expect(mock).toHaveBeenCalledTimes(0);
35+
});
36+
});
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1+
import { ButtonWrapper } from './Button.style';
2+
13
type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
24
children: React.ReactNode;
35
onClick?: React.MouseEventHandler<HTMLButtonElement>;
46
};
57

68
export function Button({ children, onClick, ...other }: ButtonProps) {
79
return (
8-
<button onClick={onClick} {...other}>
10+
<ButtonWrapper onClick={onClick} {...other}>
911
{children}
10-
</button>
12+
</ButtonWrapper>
1113
);
1214
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import styled from '@emotion/styled';
2+
3+
export const InputContainer = styled.div`
4+
display: flex;
5+
flex-direction: column;
6+
justify-content: left;
7+
gap: 0.5rem;
8+
`;
9+
10+
export const InputWrapper = styled.input`
11+
all: unset;
12+
${(props) => props.theme.fonts.body16Regular}
13+
padding: 0.5rem 0.75rem;
14+
box-shadow: 0 0 0 1px ${(props) => props.theme.colors.gray300} inset;
15+
border-radius: 0.5rem;
16+
:hover {
17+
box-shadow: 0 0 0 1px ${(props) => props.theme.colors.gray400} inset;
18+
}
19+
:focus {
20+
box-shadow: 0 0 0 2px ${(props) => props.theme.colors.green} inset;
21+
}
22+
`;
23+
24+
export const HelperText = styled.p`
25+
${(props) => props.theme.fonts.body14Regular}
26+
min-height: 1.5rem;
27+
`;
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { Input } from '.';
2+
import { render, screen } from '../../../libs/test-utils';
3+
import { describe, it } from 'vitest';
4+
5+
describe('Input', () => {
6+
it('should render helper text', () => {
7+
const inputVal = 'foo';
8+
const helperText = "I'm helping";
9+
10+
render(
11+
<Input value={inputVal} onChange={() => 1} helperText={helperText} />
12+
);
13+
14+
const helperTextElement = screen.getByText(helperText);
15+
expect(helperTextElement).toBeInTheDocument();
16+
});
17+
18+
it('should not render helper text when hideHelper is true', () => {
19+
const helperText = 'Test helper text';
20+
21+
render(
22+
<Input
23+
value={'foo'}
24+
onChange={() => 0}
25+
helperText={helperText}
26+
hideHelper
27+
/>
28+
);
29+
const helperTextElement = screen.queryByText(helperText);
30+
31+
expect(helperTextElement).not.toBeInTheDocument();
32+
});
33+
});
Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,30 @@
1+
import { InputWrapper, HelperText, InputContainer } from './Input.style';
2+
13
type InputProps = React.InputHTMLAttributes<HTMLInputElement> & {
24
value: string;
35
onChange: React.ChangeEventHandler<HTMLInputElement>;
6+
helperText?: string;
7+
hideHelper?: boolean;
8+
customRef?: React.RefObject<HTMLInputElement>;
49
};
510

6-
export function Input({ onChange, value, ...other }: InputProps) {
7-
return <input onChange={onChange} value={value} {...other} />;
11+
export function Input({
12+
onChange,
13+
value,
14+
helperText,
15+
hideHelper = false,
16+
customRef,
17+
...other
18+
}: InputProps) {
19+
return (
20+
<InputContainer>
21+
<InputWrapper
22+
onChange={onChange}
23+
value={value}
24+
ref={customRef}
25+
{...other}
26+
/>
27+
{!hideHelper && <HelperText>{helperText}</HelperText>}
28+
</InputContainer>
29+
);
830
}

src/Components/Navbar/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import { Nav, Container, List, ListItem } from './Navbar.style';
33
import { useLogin } from '../../hooks';
44

55
export function Navbar() {
6-
const { isLoggedIn } = useLogin();
7-
return <Nav>{isLoggedIn ? <LoggedInNav /> : <LoggedOutNav />}</Nav>;
6+
const { token } = useLogin();
7+
return <Nav>{token ? <LoggedInNav /> : <LoggedOutNav />}</Nav>;
88
}
99

1010
function LoggedInNav() {

src/hooks/useInput/index.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import React, { useCallback, useState } from 'react';
1+
import React, { useCallback, useRef, useState } from 'react';
22

33
export function useInput() {
44
const [inputVal, setInputVal] = useState('');
5+
const inputRef = useRef<HTMLInputElement>(null);
56

67
const changeInputVal = useCallback(
78
(e: React.ChangeEvent<HTMLInputElement>) => {
@@ -14,5 +15,9 @@ export function useInput() {
1415
setInputVal('');
1516
}, []);
1617

17-
return { inputVal, changeInputVal, resetInputVal };
18+
const focusInput = useCallback(() => {
19+
inputRef.current?.focus();
20+
}, []);
21+
22+
return { inputVal, changeInputVal, resetInputVal, focusInput, inputRef };
1823
}

src/hooks/useLogin/index.ts

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,10 @@
1-
import { atom, useAtom } from 'jotai';
1+
import { useAtom } from 'jotai';
2+
import { atomWithStorage } from 'jotai/utils';
23

3-
const loggedIn = atom(false);
4+
const tokenAtom = atomWithStorage('token', '');
45

56
export function useLogin() {
6-
const [isLoggedIn, setIsLoggedIn] = useAtom(loggedIn);
7+
const [token, setToken] = useAtom(tokenAtom);
78

8-
const login = () => {
9-
setIsLoggedIn(true);
10-
};
11-
12-
const logout = () => {
13-
setIsLoggedIn(false);
14-
};
15-
16-
return { isLoggedIn, login, logout };
9+
return { token, setToken };
1710
}

0 commit comments

Comments
 (0)