Skip to content
This repository was archived by the owner on Mar 13, 2025. It is now read-only.

Adds fixes for adding and editing payments, implements WP data reloading, updates toastr positioning #99

Merged
merged 3 commits into from
Aug 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 86 additions & 49 deletions src/components/ActionsMenu/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import PT from "prop-types";
import cn from "classnames";
import { usePopper } from "react-popper";
import Button from "components/Button";
import Tooltip from "components/Tooltip";
import IconArrowDown from "../../assets/images/icon-arrow-down-narrow.svg";
import { useClickOutside } from "utils/hooks";
import { negate, stopPropagation } from "utils/misc";
Expand Down Expand Up @@ -39,48 +40,6 @@ const ActionsMenu = ({
setIsOpen(negate);
}, []);

const onItemClick = useCallback(
(event) => {
let index = +event.target.dataset.actionIndex;
let item = items[index];
if (!item || item.disabled || item.separator) {
return;
}
closeMenu();
item.action?.();
},
[items, closeMenu]
);

const menuItems = useMemo(
() =>
items.map((item, index) => {
if (item.hidden) {
return null;
} else if (item.separator) {
return <div key={index} className={compStyles.separator} />;
} else {
return (
<div
key={index}
data-action-index={index}
onClick={onItemClick}
role="button"
tabIndex={0}
className={cn(
compStyles.item,
{ [compStyles.itemDisabled]: item.disabled },
item.className
)}
>
{item.label}
</div>
);
}
}),
[items, onItemClick]
);

return (
<div
className={compStyles.container}
Expand All @@ -104,8 +63,8 @@ const ActionsMenu = ({
</Button>
{isOpen && (
<Menu
items={menuItems}
onClickOutside={closeMenu}
close={closeMenu}
items={items}
referenceElement={referenceElement}
strategy={popupStrategy}
/>
Expand All @@ -123,6 +82,7 @@ ActionsMenu.propTypes = {
label: PT.string,
action: PT.func,
separator: PT.bool,
disabled: PT.bool,
hidden: PT.bool,
})
),
Expand All @@ -138,7 +98,7 @@ export default ActionsMenu;
* @param {Object} props component properties
* @returns {JSX.Element}
*/
const Menu = ({ items, onClickOutside, referenceElement, strategy }) => {
const Menu = ({ close, items, referenceElement, strategy }) => {
const [popperElement, setPopperElement] = useState(null);
const [arrowElement, setArrowElement] = useState(null);
const { styles, attributes } = usePopper(referenceElement, popperElement, {
Expand Down Expand Up @@ -180,7 +140,75 @@ const Menu = ({ items, onClickOutside, referenceElement, strategy }) => {
],
});

useClickOutside(popperElement, onClickOutside, []);
const onClickItem = useCallback(
(event) => {
let targetData = event.target.dataset;
let index = +targetData.actionIndex;
let item = items[index];
if (!item || targetData.disabled || item.separator) {
return;
}
close();
item.action?.();
},
[close, items]
);

useClickOutside(popperElement, close, []);

const menuItems = useMemo(() => {
return items.map((item, index) => {
if (item.hidden) {
return null;
} else if (item.separator) {
return <div key={index} className={compStyles.separator} />;
} else {
let disabled = !!item.disabled;
let reasonsDisabled = Array.isArray(item.disabled)
? item.disabled
: null;
let attrs = {
key: index,
"data-action-index": index,
onClick: onClickItem,
role: "button",
tabIndex: 0,
className: cn(
compStyles.item,
{ [compStyles.itemDisabled]: disabled },
item.className
),
};
if (disabled) {
attrs["data-disabled"] = true;
}
return (
<div {...attrs}>
{reasonsDisabled ? (
<Tooltip
content={
reasonsDisabled.length === 1 ? (
reasonsDisabled[0]
) : (
<ul>
{reasonsDisabled.map((text, index) => (
<li key={index}>{text}</li>
))}
</ul>
)
}
strategy="fixed"
>
{item.label}
</Tooltip>
) : (
item.label
)}
</div>
);
}
});
}, [items, onClickItem]);

return (
<div
Expand All @@ -189,7 +217,7 @@ const Menu = ({ items, onClickOutside, referenceElement, strategy }) => {
style={styles.popper}
{...attributes.popper}
>
<div className={compStyles.items}>{items}</div>
<div className={compStyles.items}>{menuItems}</div>
<div
ref={setArrowElement}
style={styles.arrow}
Expand All @@ -200,8 +228,17 @@ const Menu = ({ items, onClickOutside, referenceElement, strategy }) => {
};

Menu.propTypes = {
items: PT.array.isRequired,
onClickOutside: PT.func.isRequired,
close: PT.func.isRequired,
items: PT.arrayOf(
PT.shape({
label: PT.string,
action: PT.func,
checkDisabled: PT.func,
disabled: PT.bool,
separator: PT.bool,
hidden: PT.bool,
})
),
referenceElement: PT.object,
strategy: PT.oneOf(["absolute", "fixed"]),
};
5 changes: 2 additions & 3 deletions src/components/ActionsMenu/styles.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,8 @@
}

.itemDisabled {
color: gray;
opacity: 0.6;
pointer-events: none;
color: #bbb;
cursor: default;
}

.hidden {
Expand Down
63 changes: 63 additions & 0 deletions src/components/CurrencyField/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React, { useCallback } from "react";
import PT from "prop-types";
import TextField from "components/TextField";

/**
* Displays text field with optional label.
*
* @param {Object} props component properties
* @returns {JSX.Element}
*/
const CurrencyField = (props) => {
const { onChange } = props;

const onChangeValue = useCallback(
(value) => {
onChange(normalizeValue(value));
},
[onChange]
);

return <TextField {...props} onChange={onChangeValue} />;
};

CurrencyField.propTypes = {
className: PT.string,
error: PT.string,
id: PT.string,
isDisabled: PT.bool,
isTouched: PT.bool,
label: PT.string,
name: PT.string.isRequired,
onBlur: PT.func,
onChange: PT.func,
onFocus: PT.func,
size: PT.oneOf(["small", "medium"]),
value: PT.oneOfType([PT.number, PT.string]).isRequired,
};

export default CurrencyField;

/**
* Returns normalized payment amount.
*
* @param {string} value peyment amount
* @returns {string}
*/
function normalizeValue(value) {
if (!value) {
return value;
}
value = value.trim();
let dotIndex = value.lastIndexOf(".");
if (isNaN(+value) || dotIndex < 0) {
return value;
}
if (dotIndex === 0) {
return "0" + value;
}
if (value.length - dotIndex > 3) {
return value.slice(0, dotIndex + 3);
}
return value;
}
Empty file.
2 changes: 1 addition & 1 deletion src/components/Page/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import styles from "./styles.module.scss";
*/
const Page = ({ className, children }) => (
<div className={cn(styles.container, className)}>
{children}
<ReduxToastr
timeOut={TOAST_DEFAULT_TIMEOUT}
position="top-right"
Expand All @@ -26,6 +25,7 @@ const Page = ({ className, children }) => (
transitionIn="fadeIn"
transitionOut="fadeOut"
/>
{children}
</div>
);

Expand Down
1 change: 1 addition & 0 deletions src/components/Page/styles.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

@include desktop {
flex-direction: row;
flex-wrap: wrap;
}

*,
Expand Down
23 changes: 18 additions & 5 deletions src/components/TextField/index.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useCallback } from "react";
import PT from "prop-types";
import cn from "classnames";
import ValidationError from "components/ValidationError";
import styles from "./styles.module.scss";

/**
Expand All @@ -11,9 +12,12 @@ import styles from "./styles.module.scss";
*/
const TextField = ({
className,
error,
errorClassName,
id,
inputRef,
isDisabled = false,
isValid = true,
isTouched = false,
label,
name,
onBlur,
Expand All @@ -39,7 +43,7 @@ const TextField = ({
{
[styles.hasLabel]: !!label,
[styles.disabled]: isDisabled,
[styles.invalid]: !isValid,
[styles.invalid]: !!error,
},
className
)}
Expand All @@ -50,21 +54,30 @@ const TextField = ({
disabled={isDisabled}
id={id}
name={name}
type="text"
value={value}
onBlur={onBlur}
onChange={onInputChange}
onFocus={onFocus}
ref={inputRef}
type="text"
value={value}
/>
{isTouched && error && (
<ValidationError className={cn(styles.error, errorClassName)}>
{error}
</ValidationError>
)}
</div>
);
};

TextField.propTypes = {
className: PT.string,
error: PT.string,
errorClassName: PT.string,
id: PT.string,
inputRef: PT.oneOfType([PT.object, PT.func]),
isDisabled: PT.bool,
isValid: PT.bool,
isTouched: PT.bool,
label: PT.string,
name: PT.string.isRequired,
onBlur: PT.func,
Expand Down
4 changes: 2 additions & 2 deletions src/components/TextField/styles.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@
}

&.invalid {
input,
input:hover {
input.input,
input.input:hover {
border-color: $error-color;
color: $error-text-color;
}
Expand Down
11 changes: 10 additions & 1 deletion src/components/Tooltip/styles.module.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
@import "styles/variables";
@import "styles/mixins";

.container {
position: relative;
display: inline-flex;
Expand All @@ -14,8 +17,14 @@
border-radius: 8px;
padding: 10px 15px;
line-height: 22px;
box-shadow: 0px 5px 25px #c6c6c6;
font-size: $font-size-px;
@include roboto-regular;
font-weight: normal;
letter-spacing: normal;
text-transform: none;
color: $text-color;
background: #fff;
box-shadow: 0px 5px 25px #c6c6c6;

ul {
margin: 0;
Expand Down
2 changes: 1 addition & 1 deletion src/components/ValidationError/styles.module.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
.container {
margin: 10px 0;
margin: 10px 0 0;
border: 1px solid #ffd5d1;
padding: 9px 10px;
min-height: 40px;
Expand Down
Loading