-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: AppXxx components feat: AppStore feat: Routing feat: Utils feat: Tests
- Loading branch information
Showing
55 changed files
with
2,549 additions
and
69 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
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 was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
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 |
---|---|---|
@@ -1,22 +1,24 @@ | ||
import './App.css'; | ||
import { AppStore } from './store'; | ||
import { AppRouter, Routes } from './routes'; | ||
import { ErrorBoundary } from './components'; | ||
import { AppThemeProvider } from './theme'; | ||
|
||
/** | ||
* Renders Main Application | ||
* Root Application Component | ||
* @class App | ||
*/ | ||
const App = () => { | ||
return ( | ||
<div className="App"> | ||
<header> | ||
Currency Exchange Sample | ||
</header> | ||
<main> | ||
Main content here... | ||
</main> | ||
<footer> | ||
Copyright © <a href="https://karpolan.com" target="_blank" rel="noreferrer noopener">KARPOLAN</a> | ||
</footer> | ||
</div> | ||
<ErrorBoundary name="App"> | ||
<AppStore> | ||
<AppThemeProvider> | ||
<AppRouter> | ||
<Routes /> | ||
</AppRouter> | ||
</AppThemeProvider> | ||
</AppStore> | ||
</ErrorBoundary> | ||
); | ||
} | ||
}; | ||
|
||
export default App; |
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,31 @@ | ||
import clsx from 'clsx'; | ||
import { Theme, makeStyles } from '@material-ui/core/styles'; | ||
import MuiAlert, { AlertProps as MuiAlertProps } from '@material-ui/lab/Alert'; | ||
|
||
const APP_ALERT_SEVERITY = 'info'; // 'error' | 'info'| 'success' | 'warning' | ||
const APP_ALERT_VARIANT = 'standard'; // 'filled' | 'outlined' | 'standard' | ||
|
||
const useStyles = makeStyles((theme: Theme) => ({ | ||
root: { | ||
marginTop: theme.spacing(1), | ||
marginBottom: theme.spacing(1), | ||
}, | ||
})); | ||
|
||
/** | ||
* Application styled Alert component | ||
*/ | ||
const AppAlert: React.FC<MuiAlertProps> = ({ | ||
severity = APP_ALERT_SEVERITY, | ||
variant = APP_ALERT_VARIANT, | ||
className, | ||
onClose, | ||
...restOfProps | ||
}) => { | ||
const classes = useStyles(); | ||
const classRoot = clsx(classes.root, className); | ||
|
||
return <MuiAlert className={classRoot} severity={severity} variant={variant} onClose={onClose} {...restOfProps} />; | ||
}; | ||
|
||
export default AppAlert; |
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,3 @@ | ||
import AppAlert from './AppAlert'; | ||
|
||
export { AppAlert as default, AppAlert }; |
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,81 @@ | ||
import { render, screen } from '@testing-library/react'; | ||
import AppButton from './AppButton'; | ||
import { ColorName } from '../../utils/style'; | ||
|
||
/** | ||
* Test specific color for AppButton | ||
* @param {string} colorName - name of the color, one of ColorName type | ||
* @param {string} [expectedClassName] - optional value to be found in className (color "true" may use "success" class name) | ||
* @param {boolean} [ignoreClassName] - optional flag to ignore className (color "inherit" doesn't use any class name) | ||
*/ | ||
function testButtonColor(colorName: string, expectedClassName = colorName, ignoreClassName = false) { | ||
it(`supports "${colorName}" color`, async () => { | ||
let text = `${colorName} button`; | ||
await render(<AppButton color={colorName as ColorName}>{text}</AppButton>); | ||
|
||
let span = await screen.getByText(text); // <span> with specific text | ||
expect(span).toBeDefined(); | ||
|
||
let button = await span.closest('button'); // parent <button> element | ||
expect(button).toBeDefined(); | ||
if (!ignoreClassName) { | ||
expect(button?.className?.includes(`makeStyles-${expectedClassName}`)).toBeTruthy(); // There is "makeStyles-[expectedClassName]-xxx" class | ||
} | ||
}); | ||
} | ||
|
||
describe('AppButton component', () => { | ||
// beforeEach(() => {}); | ||
|
||
it('renders itself', async () => { | ||
let text = 'sample button'; | ||
await render(<AppButton>{text}</AppButton>); | ||
let span = await screen.getByText(text); | ||
expect(span).toBeDefined(); | ||
expect(span).toHaveTextContent(text); | ||
let button = await span.closest('button'); // parent <button> element | ||
expect(button).toBeDefined(); | ||
expect(button).toHaveAttribute('type', 'button'); // not "submit" or "input" by default | ||
}); | ||
|
||
testButtonColor('primary'); | ||
testButtonColor('secondary'); | ||
testButtonColor('error'); | ||
testButtonColor('warning'); | ||
testButtonColor('info'); | ||
testButtonColor('success'); | ||
testButtonColor('true'); | ||
testButtonColor('false'); | ||
|
||
testButtonColor('default'); | ||
testButtonColor('inherit', 'default', true); | ||
|
||
it('supports className property', async () => { | ||
let text = 'button with specific class'; | ||
let className = 'someClassName'; | ||
await render(<AppButton className={className}>{text}</AppButton>); | ||
let span = await screen.getByText(text); | ||
expect(span).toBeDefined(); | ||
let button = await span.closest('button'); // parent <button> element | ||
expect(button).toBeDefined(); | ||
expect(button).toHaveClass(className); | ||
}); | ||
|
||
it('supports label property', async () => { | ||
let text = 'button with label'; | ||
await render(<AppButton label={text} />); | ||
let span = await screen.getByText(text); | ||
expect(span).toBeDefined(); | ||
let button = await span.closest('button'); // parent <button> element | ||
expect(button).toBeDefined(); | ||
}); | ||
|
||
it('supports text property', async () => { | ||
let text = 'button with text'; | ||
await render(<AppButton text={text} />); | ||
let span = await screen.getByText(text); | ||
expect(span).toBeDefined(); | ||
let button = await span.closest('button'); // parent <button> element | ||
expect(button).toBeDefined(); | ||
}); | ||
}); |
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,64 @@ | ||
import clsx from 'clsx'; | ||
import { Theme, makeStyles } from '@material-ui/core/styles'; | ||
import Box from '@material-ui/core/Box'; | ||
import Button, { ButtonProps } from '@material-ui/core/Button'; | ||
import { buttonStylesByNames, ColorName } from '../../utils/style'; | ||
|
||
const APP_BUTTON_VARIANT = 'contained'; // | 'text' | 'outlined' | ||
|
||
const useStyles = makeStyles((theme: Theme) => ({ | ||
box: { | ||
display: 'inline-block', | ||
}, | ||
// Add "filled" styles for Material UI names 'primary', 'secondary', 'warning', and so on | ||
...buttonStylesByNames(theme), | ||
})); | ||
|
||
interface Props extends Omit<ButtonProps, 'color'> { | ||
color?: ColorName | 'inherit'; | ||
label?: string; // Alternate to text | ||
text?: string; // Alternate to label | ||
m?: number; | ||
mt?: number; | ||
mb?: number; | ||
ml?: number; | ||
mr?: number; | ||
// Missing props | ||
component?: React.ElementType; // Could be RouterLink, AppLink, etc. | ||
to?: string; // Link prop | ||
href?: string; // Link prop | ||
} | ||
|
||
/** | ||
* Application styled Material UI Button | ||
* @class AppButton | ||
* @param {string} [color] - name of color from Material UI palette 'primary', 'secondary', 'warning', and so on | ||
* @param {string} [children] - content to render, overrides .label and .text | ||
* @param {string} [label] - text to render, alternate to .text | ||
* @param {string} [text] - text to render, alternate to .label | ||
*/ | ||
const AppButton: React.FC<Props> = ({ | ||
className, | ||
children, | ||
color = 'default', | ||
label, | ||
text, | ||
m = 0, | ||
mt = 1, | ||
mb = 1, | ||
ml = 1, | ||
mr = 1, | ||
...restOfProps | ||
}) => { | ||
const classes = useStyles(); | ||
const classButton = clsx(classes[color as ColorName], className); | ||
return ( | ||
<Box {...{ m, mt, mb, ml, mr }} className={classes.box}> | ||
<Button className={classButton} variant={APP_BUTTON_VARIANT} {...restOfProps}> | ||
{children || label || text} | ||
</Button> | ||
</Box> | ||
); | ||
}; | ||
|
||
export default AppButton; |
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,3 @@ | ||
import AppButton from './AppButton'; | ||
|
||
export { AppButton as default, AppButton }; |
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,67 @@ | ||
import { SvgIcon } from '@material-ui/core'; | ||
// SVG assets | ||
import { ReactComponent as LogoIcon } from './logo.svg'; | ||
// Material Icons | ||
import DefaultIcon from '@material-ui/icons/MoreHoriz'; | ||
import SettingsIcon from '@material-ui/icons/Settings'; | ||
import VisibilityIcon from '@material-ui/icons/Visibility'; | ||
import VisibilityOffIcon from '@material-ui/icons/VisibilityOff'; | ||
import MenuIcon from '@material-ui/icons/Menu'; | ||
import CloseIcon from '@material-ui/icons/Close'; | ||
import DayNightIcon from '@material-ui/icons/Brightness4'; | ||
import NightIcon from '@material-ui/icons/Brightness3'; | ||
import DayIcon from '@material-ui/icons/Brightness5'; | ||
import SearchIcon from '@material-ui/icons/Search'; | ||
import InfoIcon from '@material-ui/icons/Info'; | ||
import HomeIcon from '@material-ui/icons/Home'; | ||
import AccountCircle from '@material-ui/icons/AccountCircle'; | ||
import PersonAddIcon from '@material-ui/icons/PersonAdd'; | ||
import PersonIcon from '@material-ui/icons/Person'; | ||
|
||
/** | ||
* How to use: | ||
* 1. Import all required MUI or other SVG icons into this file. | ||
* 2. Add icons with "unique lowercase names" into ICONS object. | ||
* 3. Use icons everywhere in the App by their names in <AppIcon name="xxx" /> component | ||
* Important: properties of ICONS object MUST be lowercase! | ||
* Note: You can use camelCase or UPPERCASE in the <AppIcon name="someIconByName" /> component | ||
*/ | ||
const ICONS: Record<string, React.ComponentType> = { | ||
default: DefaultIcon, | ||
logo: () => ( | ||
<SvgIcon> | ||
<LogoIcon /> | ||
</SvgIcon> | ||
), | ||
close: CloseIcon, | ||
menu: MenuIcon, | ||
settings: SettingsIcon, | ||
visibilityon: VisibilityIcon, | ||
visibilityoff: VisibilityOffIcon, | ||
daynight: DayNightIcon, | ||
night: NightIcon, | ||
day: DayIcon, | ||
search: SearchIcon, | ||
info: InfoIcon, | ||
home: HomeIcon, | ||
account: AccountCircle, | ||
signup: PersonAddIcon, | ||
login: PersonIcon, | ||
}; | ||
|
||
/** | ||
* Renders SVG icon by given Icon name | ||
* @param {string} [props.name] - name of the Icon to render | ||
* @param {string} [props.icon] - name of the Icon to render | ||
*/ | ||
interface Props { | ||
name?: string; // Icon's name | ||
icon?: string; // Icon's name alternate prop | ||
} | ||
const AppIcon: React.FC<Props> = ({ name, icon, ...restOfProps }) => { | ||
const iconName = (name || icon || 'default').trim().toLowerCase(); | ||
const ComponentToRender = ICONS[iconName] || DefaultIcon; | ||
return <ComponentToRender {...restOfProps} />; | ||
}; | ||
|
||
export default AppIcon; |
Oops, something went wrong.