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

Commit 95bf062

Browse files
authored
Merge pull request #99 from MadOPcode/dev
Adds fixes for adding and editing payments, implements WP data reloading, updates toastr positioning
2 parents dcdd3c0 + db140e4 commit 95bf062

File tree

23 files changed

+419
-170
lines changed

23 files changed

+419
-170
lines changed

src/components/ActionsMenu/index.jsx

+86-49
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import PT from "prop-types";
33
import cn from "classnames";
44
import { usePopper } from "react-popper";
55
import Button from "components/Button";
6+
import Tooltip from "components/Tooltip";
67
import IconArrowDown from "../../assets/images/icon-arrow-down-narrow.svg";
78
import { useClickOutside } from "utils/hooks";
89
import { negate, stopPropagation } from "utils/misc";
@@ -39,48 +40,6 @@ const ActionsMenu = ({
3940
setIsOpen(negate);
4041
}, []);
4142

42-
const onItemClick = useCallback(
43-
(event) => {
44-
let index = +event.target.dataset.actionIndex;
45-
let item = items[index];
46-
if (!item || item.disabled || item.separator) {
47-
return;
48-
}
49-
closeMenu();
50-
item.action?.();
51-
},
52-
[items, closeMenu]
53-
);
54-
55-
const menuItems = useMemo(
56-
() =>
57-
items.map((item, index) => {
58-
if (item.hidden) {
59-
return null;
60-
} else if (item.separator) {
61-
return <div key={index} className={compStyles.separator} />;
62-
} else {
63-
return (
64-
<div
65-
key={index}
66-
data-action-index={index}
67-
onClick={onItemClick}
68-
role="button"
69-
tabIndex={0}
70-
className={cn(
71-
compStyles.item,
72-
{ [compStyles.itemDisabled]: item.disabled },
73-
item.className
74-
)}
75-
>
76-
{item.label}
77-
</div>
78-
);
79-
}
80-
}),
81-
[items, onItemClick]
82-
);
83-
8443
return (
8544
<div
8645
className={compStyles.container}
@@ -104,8 +63,8 @@ const ActionsMenu = ({
10463
</Button>
10564
{isOpen && (
10665
<Menu
107-
items={menuItems}
108-
onClickOutside={closeMenu}
66+
close={closeMenu}
67+
items={items}
10968
referenceElement={referenceElement}
11069
strategy={popupStrategy}
11170
/>
@@ -123,6 +82,7 @@ ActionsMenu.propTypes = {
12382
label: PT.string,
12483
action: PT.func,
12584
separator: PT.bool,
85+
disabled: PT.bool,
12686
hidden: PT.bool,
12787
})
12888
),
@@ -138,7 +98,7 @@ export default ActionsMenu;
13898
* @param {Object} props component properties
13999
* @returns {JSX.Element}
140100
*/
141-
const Menu = ({ items, onClickOutside, referenceElement, strategy }) => {
101+
const Menu = ({ close, items, referenceElement, strategy }) => {
142102
const [popperElement, setPopperElement] = useState(null);
143103
const [arrowElement, setArrowElement] = useState(null);
144104
const { styles, attributes } = usePopper(referenceElement, popperElement, {
@@ -180,7 +140,75 @@ const Menu = ({ items, onClickOutside, referenceElement, strategy }) => {
180140
],
181141
});
182142

183-
useClickOutside(popperElement, onClickOutside, []);
143+
const onClickItem = useCallback(
144+
(event) => {
145+
let targetData = event.target.dataset;
146+
let index = +targetData.actionIndex;
147+
let item = items[index];
148+
if (!item || targetData.disabled || item.separator) {
149+
return;
150+
}
151+
close();
152+
item.action?.();
153+
},
154+
[close, items]
155+
);
156+
157+
useClickOutside(popperElement, close, []);
158+
159+
const menuItems = useMemo(() => {
160+
return items.map((item, index) => {
161+
if (item.hidden) {
162+
return null;
163+
} else if (item.separator) {
164+
return <div key={index} className={compStyles.separator} />;
165+
} else {
166+
let disabled = !!item.disabled;
167+
let reasonsDisabled = Array.isArray(item.disabled)
168+
? item.disabled
169+
: null;
170+
let attrs = {
171+
key: index,
172+
"data-action-index": index,
173+
onClick: onClickItem,
174+
role: "button",
175+
tabIndex: 0,
176+
className: cn(
177+
compStyles.item,
178+
{ [compStyles.itemDisabled]: disabled },
179+
item.className
180+
),
181+
};
182+
if (disabled) {
183+
attrs["data-disabled"] = true;
184+
}
185+
return (
186+
<div {...attrs}>
187+
{reasonsDisabled ? (
188+
<Tooltip
189+
content={
190+
reasonsDisabled.length === 1 ? (
191+
reasonsDisabled[0]
192+
) : (
193+
<ul>
194+
{reasonsDisabled.map((text, index) => (
195+
<li key={index}>{text}</li>
196+
))}
197+
</ul>
198+
)
199+
}
200+
strategy="fixed"
201+
>
202+
{item.label}
203+
</Tooltip>
204+
) : (
205+
item.label
206+
)}
207+
</div>
208+
);
209+
}
210+
});
211+
}, [items, onClickItem]);
184212

185213
return (
186214
<div
@@ -189,7 +217,7 @@ const Menu = ({ items, onClickOutside, referenceElement, strategy }) => {
189217
style={styles.popper}
190218
{...attributes.popper}
191219
>
192-
<div className={compStyles.items}>{items}</div>
220+
<div className={compStyles.items}>{menuItems}</div>
193221
<div
194222
ref={setArrowElement}
195223
style={styles.arrow}
@@ -200,8 +228,17 @@ const Menu = ({ items, onClickOutside, referenceElement, strategy }) => {
200228
};
201229

202230
Menu.propTypes = {
203-
items: PT.array.isRequired,
204-
onClickOutside: PT.func.isRequired,
231+
close: PT.func.isRequired,
232+
items: PT.arrayOf(
233+
PT.shape({
234+
label: PT.string,
235+
action: PT.func,
236+
checkDisabled: PT.func,
237+
disabled: PT.bool,
238+
separator: PT.bool,
239+
hidden: PT.bool,
240+
})
241+
),
205242
referenceElement: PT.object,
206243
strategy: PT.oneOf(["absolute", "fixed"]),
207244
};

src/components/ActionsMenu/styles.module.scss

+2-3
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,8 @@
7171
}
7272

7373
.itemDisabled {
74-
color: gray;
75-
opacity: 0.6;
76-
pointer-events: none;
74+
color: #bbb;
75+
cursor: default;
7776
}
7877

7978
.hidden {
+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import React, { useCallback } from "react";
2+
import PT from "prop-types";
3+
import TextField from "components/TextField";
4+
5+
/**
6+
* Displays text field with optional label.
7+
*
8+
* @param {Object} props component properties
9+
* @returns {JSX.Element}
10+
*/
11+
const CurrencyField = (props) => {
12+
const { onChange } = props;
13+
14+
const onChangeValue = useCallback(
15+
(value) => {
16+
onChange(normalizeValue(value));
17+
},
18+
[onChange]
19+
);
20+
21+
return <TextField {...props} onChange={onChangeValue} />;
22+
};
23+
24+
CurrencyField.propTypes = {
25+
className: PT.string,
26+
error: PT.string,
27+
id: PT.string,
28+
isDisabled: PT.bool,
29+
isTouched: PT.bool,
30+
label: PT.string,
31+
name: PT.string.isRequired,
32+
onBlur: PT.func,
33+
onChange: PT.func,
34+
onFocus: PT.func,
35+
size: PT.oneOf(["small", "medium"]),
36+
value: PT.oneOfType([PT.number, PT.string]).isRequired,
37+
};
38+
39+
export default CurrencyField;
40+
41+
/**
42+
* Returns normalized payment amount.
43+
*
44+
* @param {string} value peyment amount
45+
* @returns {string}
46+
*/
47+
function normalizeValue(value) {
48+
if (!value) {
49+
return value;
50+
}
51+
value = value.trim();
52+
let dotIndex = value.lastIndexOf(".");
53+
if (isNaN(+value) || dotIndex < 0) {
54+
return value;
55+
}
56+
if (dotIndex === 0) {
57+
return "0" + value;
58+
}
59+
if (value.length - dotIndex > 3) {
60+
return value.slice(0, dotIndex + 3);
61+
}
62+
return value;
63+
}

src/components/CurrencyField/styles.module.scss

Whitespace-only changes.

src/components/Page/index.jsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import styles from "./styles.module.scss";
1515
*/
1616
const Page = ({ className, children }) => (
1717
<div className={cn(styles.container, className)}>
18-
{children}
1918
<ReduxToastr
2019
timeOut={TOAST_DEFAULT_TIMEOUT}
2120
position="top-right"
@@ -26,6 +25,7 @@ const Page = ({ className, children }) => (
2625
transitionIn="fadeIn"
2726
transitionOut="fadeOut"
2827
/>
28+
{children}
2929
</div>
3030
);
3131

src/components/Page/styles.module.scss

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
@include desktop {
1616
flex-direction: row;
17+
flex-wrap: wrap;
1718
}
1819

1920
*,

src/components/TextField/index.jsx

+18-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React, { useCallback } from "react";
22
import PT from "prop-types";
33
import cn from "classnames";
4+
import ValidationError from "components/ValidationError";
45
import styles from "./styles.module.scss";
56

67
/**
@@ -11,9 +12,12 @@ import styles from "./styles.module.scss";
1112
*/
1213
const TextField = ({
1314
className,
15+
error,
16+
errorClassName,
1417
id,
18+
inputRef,
1519
isDisabled = false,
16-
isValid = true,
20+
isTouched = false,
1721
label,
1822
name,
1923
onBlur,
@@ -39,7 +43,7 @@ const TextField = ({
3943
{
4044
[styles.hasLabel]: !!label,
4145
[styles.disabled]: isDisabled,
42-
[styles.invalid]: !isValid,
46+
[styles.invalid]: !!error,
4347
},
4448
className
4549
)}
@@ -50,21 +54,30 @@ const TextField = ({
5054
disabled={isDisabled}
5155
id={id}
5256
name={name}
53-
type="text"
54-
value={value}
5557
onBlur={onBlur}
5658
onChange={onInputChange}
5759
onFocus={onFocus}
60+
ref={inputRef}
61+
type="text"
62+
value={value}
5863
/>
64+
{isTouched && error && (
65+
<ValidationError className={cn(styles.error, errorClassName)}>
66+
{error}
67+
</ValidationError>
68+
)}
5969
</div>
6070
);
6171
};
6272

6373
TextField.propTypes = {
6474
className: PT.string,
75+
error: PT.string,
76+
errorClassName: PT.string,
6577
id: PT.string,
78+
inputRef: PT.oneOfType([PT.object, PT.func]),
6679
isDisabled: PT.bool,
67-
isValid: PT.bool,
80+
isTouched: PT.bool,
6881
label: PT.string,
6982
name: PT.string.isRequired,
7083
onBlur: PT.func,

src/components/TextField/styles.module.scss

+2-2
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@
3838
}
3939

4040
&.invalid {
41-
input,
42-
input:hover {
41+
input.input,
42+
input.input:hover {
4343
border-color: $error-color;
4444
color: $error-text-color;
4545
}

src/components/Tooltip/styles.module.scss

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
@import "styles/variables";
2+
@import "styles/mixins";
3+
14
.container {
25
position: relative;
36
display: inline-flex;
@@ -14,8 +17,14 @@
1417
border-radius: 8px;
1518
padding: 10px 15px;
1619
line-height: 22px;
17-
box-shadow: 0px 5px 25px #c6c6c6;
20+
font-size: $font-size-px;
21+
@include roboto-regular;
22+
font-weight: normal;
23+
letter-spacing: normal;
24+
text-transform: none;
25+
color: $text-color;
1826
background: #fff;
27+
box-shadow: 0px 5px 25px #c6c6c6;
1928

2029
ul {
2130
margin: 0;

src/components/ValidationError/styles.module.scss

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
.container {
2-
margin: 10px 0;
2+
margin: 10px 0 0;
33
border: 1px solid #ffd5d1;
44
padding: 9px 10px;
55
min-height: 40px;

0 commit comments

Comments
 (0)