Skip to content

Commit

Permalink
REACT-326 Button loader added + fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
IvanHoncharenko committed Apr 12, 2023
1 parent e180943 commit 4a7832f
Show file tree
Hide file tree
Showing 10 changed files with 204 additions and 40 deletions.
4 changes: 1 addition & 3 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,7 @@
}
],
"jsx-a11y/label-has-for": "off",
"react/jsx-props-no-spreading": ["error", {
"exceptions": ["input"]
}],
"react/jsx-props-no-spreading": "off",
"react/jsx-uses-react": "off",
"react/react-in-jsx-scope": "off",
"react/function-component-definition": [
Expand Down
65 changes: 44 additions & 21 deletions src/components/Button/Button.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,58 @@
import React, { FC, memo } from 'react';
import React, { FC, memo, useMemo } from 'react';
import { IButtonProps } from './interfaces/IButton';
import { EColors, ESizes, EVariants } from '../../constants/tsConstants';
import styles from './sass/Button.module.scss';
import BallPulse from '../Loaders/BallPulse/BallPulse';

const Button: FC<IButtonProps> = ({
label = '',
text = '',
aliaLabel = '',
disabled = false,
onPress,
variant = 'contained',
color = 'primary',
size = 'medium',
isLoading = false,
onClick,
onMouseEnter,
onMouseLeave,
variant = EVariants.CONTAINED,
color = EColors.PRIMARY,
size = ESizes.MEDIUM,
iconLeft = null,
iconRight = null,
buttonClass = '',
}) => (
<button
className={`
className = '',
}) => {
const loader = useMemo(() => {
if (isLoading) {
return (
<BallPulse
className={styles.loader}
color={variant === EVariants.CONTAINED || disabled ? undefined : color}
/>
);
}
return null;
}, [isLoading, disabled, variant, color]);

return (
<button
aria-label={aliaLabel}
type="button"
className={`
${styles.button}
${styles[variant]}
${styles[color]}
${styles[size]}
${disabled ? styles.disabled : ''}
${buttonClass}
${className}
`}
onClick={onPress}
disabled={disabled}
type="button"
>
{iconLeft && <img className={styles.iconLeft} src={iconLeft} alt={label || ''} />}
{label && <span>{label}</span>}
{iconRight && <img className={styles.iconRight} src={iconRight} alt={label || ''} />}
</button>
);

disabled={disabled || isLoading}
onClick={onClick}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
>
{iconLeft && <img className={styles.iconLeft} src={iconLeft} alt={text} />}
{text && <span className={isLoading ? styles.loaderText : ''}>{text}</span>}
{loader}
{iconRight && <img className={styles.iconRight} src={iconRight} alt={text} />}
</button>
);
};
export default memo(Button);
19 changes: 12 additions & 7 deletions src/components/Button/interfaces/IButton.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import { EColors, EVariants, EButtonSizes } from '../../../constants/tsConstants';

export interface IButtonProps {
label?: string,
onPress: () => void,
text?: string,
aliaLabel?: string,
disabled?: boolean,
styles?: string,
variant: 'text' | 'contained' | 'outlined',
color: 'primary' | 'warning' | 'error',
size: 'small' | 'medium' | 'big' | 'large',
isLoading?: boolean,
onClick?: () => void,
onMouseEnter?: () => void,
onMouseLeave?: () => void,
variant: EVariants,
color: EColors,
size: EButtonSizes,
iconLeft?: string,
iconRight?: string,
buttonClass?: string,
className?: string,
}
29 changes: 21 additions & 8 deletions src/components/Button/sass/Button.module.scss
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
@import "../../../sass/colors";
@import "../../../sass/main";

.button {
position: relative;
color: $white;
background-color: $primary;
font-family: 'Inter', sans-serif;
font-weight: 600;
font-size: 14px;
cursor: pointer;
Expand All @@ -18,18 +19,18 @@
&.contained {
&.primary {
background-color: $primary;
transition: all .3s;

&:hover {
transition: background-color .5s,color .5s,border .5s;
background-color: $primary-hover;
}
}

&.warning {
background-color: $warning;
transition: all .3s;

&:hover {
transition: background-color .5s,color .5s,border .5s;
background-color: $warning-hover;
}
}
Expand Down Expand Up @@ -62,30 +63,29 @@
&.primary {
border-color: $light-gray;
color: $primary;
transition: all .3s;

&:hover {
transition: background-color .5s,color .5s,border .5s;
background-color: $light-blue;
}
}

&.warning {
border-color: $warning;
color: $warning;
transition: all .3s;

&:hover {
transition: background-color .5s,color .5s,border .5s;
background: $warning-hover;
opacity: 70%;
}
}

&.error {
border-color: $error;
color: $error;
transition: all .3s;

&:hover {
transition: background-color .5s,color .5s,border .5s;
background-color: $error-background;
}
}
Expand All @@ -106,9 +106,22 @@
}

&.disabled {
cursor: default;
transition: initial;
cursor: not-allowed;
background: $light-gray!important;
color: $gray!important;
border: none;
}

.loaderText {
visibility: hidden;
}

.loader {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
}

.iconLeft {
Expand Down
17 changes: 17 additions & 0 deletions src/components/Loaders/BallPulse/BallPulse.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React, { FC } from 'react';
import { IBallPulseProps } from './interfaces/IBallPulse';
import { EBallPulseColorsExtender } from '../../../constants/tsConstants';
import styles from './sass/BallPulse.module.scss';

const BallPulse: FC<IBallPulseProps> = ({
className = '',
color = EBallPulseColorsExtender.WHITE,
}) => (
<div className={`${styles.wrapper} ${styles[color]} ${className}`}>
<div />
<div />
<div />
</div>
);

export default BallPulse;
6 changes: 6 additions & 0 deletions src/components/Loaders/BallPulse/interfaces/IBallPulse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { EBallPulseColors } from '../../../../constants/tsConstants';

export interface IBallPulseProps {
className?: string,
color?: EBallPulseColors,
}
69 changes: 69 additions & 0 deletions src/components/Loaders/BallPulse/sass/BallPulse.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
@import '../../../../sass/colors';

.wrapper {
display: flex;

&.white {
> div {
background-color: $white;
}
}

&.primary {
> div {
background-color: $primary;
}
}

&.success {
> div {
background-color: $success;
}
}

&.warning {
> div {
background-color: $warning;
}
}

&.error {
> div {
background-color: $error;
}
}

> div {
width: 8px;
height: 8px;

border-radius: 100%;
margin: 2px;
display: inline-block;
-webkit-animation-fill-mode: both;
animation-fill-mode: both;

&:nth-child(1) {
animation: scale .75s -.24s infinite cubic-bezier(.2,.68,.18,1.08);
}

&:nth-child(2) {
animation: scale .75s -.12s infinite cubic-bezier(.2,.68,.18,1.08);
}

&:nth-child(3) {
animation: scale .75s 0s infinite cubic-bezier(.2,.68,.18,1.08);
}
}
}

@keyframes scale {
30% {
-webkit-transform: scale(.3);
transform: scale(.3);
}
100% {
-webkit-transform: scale(1);
transform: scale(1);
}
}
30 changes: 30 additions & 0 deletions src/constants/tsConstants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
export enum ESizes {
SMALL = 'small',
MEDIUM = 'medium',
BIG = 'big',
}

export enum EVariants {
TEXT = 'text',
CONTAINED = 'contained',
OUTLINED = 'outlined',
}

export enum EColors {
PRIMARY = 'primary',
WARNING = 'warning',
ERROR = 'error',
}

enum EButtonSizesExtender {
LARGE = 'large',
}

export type EButtonSizes = ESizes | EButtonSizesExtender;

export enum EBallPulseColorsExtender {
WHITE = 'white',
SUCCESS = 'success',
}

export type EBallPulseColors = EColors | EBallPulseColorsExtender;
3 changes: 3 additions & 0 deletions src/sass/main.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
* {
font-family: -apple-system,Inter,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;
}
2 changes: 1 addition & 1 deletion src/stories/Button.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ const Template: ComponentStory<typeof Button> = (args) => <Button {...args} />;

export const Default = Template.bind({});
Default.args = {
label: 'Default',
text: 'Button',
};

0 comments on commit 4a7832f

Please sign in to comment.