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

Commit ef948a0

Browse files
authored
Merge pull request #89 from topcoder-platform/feature/payment-updates
Payment Updates
2 parents 73252ea + 345eb9e commit ef948a0

File tree

48 files changed

+2161
-92
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+2161
-92
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"dev-https": "cross-env APPMODE=development webpack-dev-server --https --port 8502 --host 0.0.0.0",
77
"build": "webpack --mode=${APPMODE:-production} --env.config=${APPENV:-prod}",
88
"analyze": "webpack --mode=production --env.analyze=true",
9-
"lint": "eslint src --ext js --ext jsx",
9+
"lint": "eslint ./src --ext .js,.jsx",
1010
"format": "prettier --write \"./**\"",
1111
"test": "cross-env BABEL_ENV=test jest",
1212
"watch-tests": "cross-env BABEL_ENV=test jest --watch",
Loading

src/components/ActionsMenu/index.jsx

+202
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
import React, { useCallback, useMemo, useState } from "react";
2+
import PT from "prop-types";
3+
import cn from "classnames";
4+
import { usePopper } from "react-popper";
5+
import Button from "components/Button";
6+
import IconArrowDown from "../../assets/images/icon-arrow-down-narrow.svg";
7+
import { useClickOutside } from "utils/hooks";
8+
import { negate, stopPropagation } from "utils/misc";
9+
import compStyles from "./styles.module.scss";
10+
11+
/**
12+
* Displays a clickable button with a menu.
13+
*
14+
* @param {Object} props component properties
15+
* @param {'primary'|'error'|'warning'} [props.handleColor] menu handle color
16+
* @param {'small'|'medium'} [props.handleSize] menu handle size
17+
* @param {Array} props.items menu items
18+
* @param {'absolute'|'fixed'} [props.popupStrategy] popup positioning strategy
19+
* @param {boolean} [props.stopClickPropagation] whether to stop click event propagation
20+
* @returns {JSX.Element}
21+
*/
22+
const ActionsMenu = ({
23+
handleColor = "primary",
24+
handleSize = "small",
25+
items = [],
26+
popupStrategy = "absolute",
27+
stopClickPropagation = false,
28+
}) => {
29+
const [isOpen, setIsOpen] = useState(false);
30+
const [referenceElement, setReferenceElement] = useState(null);
31+
32+
const closeMenu = useCallback(() => {
33+
setIsOpen(false);
34+
}, []);
35+
36+
const toggleMenu = useCallback(() => {
37+
setIsOpen(negate);
38+
}, []);
39+
40+
const onItemClick = useCallback(
41+
(event) => {
42+
let index = +event.target.dataset.actionIndex;
43+
let item = items[index];
44+
if (!item || item.disabled || item.separator) {
45+
return;
46+
}
47+
closeMenu();
48+
item.action?.();
49+
},
50+
[items, closeMenu]
51+
);
52+
53+
const menuItems = useMemo(
54+
() =>
55+
items.map((item, index) => {
56+
if (item.hidden) {
57+
return null;
58+
} else if (item.separator) {
59+
return <div key={index} className={compStyles.separator} />;
60+
} else {
61+
return (
62+
<div
63+
key={index}
64+
data-action-index={index}
65+
onClick={onItemClick}
66+
role="button"
67+
tabIndex={0}
68+
className={cn(
69+
compStyles.item,
70+
{ [compStyles.itemDisabled]: item.disabled },
71+
item.className
72+
)}
73+
>
74+
{item.label}
75+
</div>
76+
);
77+
}
78+
}),
79+
[items, onItemClick]
80+
);
81+
82+
return (
83+
<div
84+
className={compStyles.container}
85+
onClick={stopClickPropagation ? stopPropagation : null}
86+
role="button"
87+
tabIndex={0}
88+
>
89+
<Button
90+
color={handleColor}
91+
size={handleSize}
92+
variant="contained"
93+
onClick={isOpen ? null : toggleMenu}
94+
className={cn(compStyles.handle, {
95+
[compStyles.handleMenuOpen]: isOpen,
96+
})}
97+
innerRef={setReferenceElement}
98+
>
99+
Actions <IconArrowDown className={compStyles.iconArrowDown} />
100+
</Button>
101+
{isOpen && (
102+
<Menu
103+
items={menuItems}
104+
onClickOutside={closeMenu}
105+
referenceElement={referenceElement}
106+
strategy={popupStrategy}
107+
/>
108+
)}
109+
</div>
110+
);
111+
};
112+
113+
ActionsMenu.propTypes = {
114+
handleColor: PT.oneOf(["primary", "error", "warning"]),
115+
handleSize: PT.oneOf(["small", "medium"]),
116+
items: PT.arrayOf(
117+
PT.shape({
118+
label: PT.string,
119+
action: PT.func,
120+
separator: PT.bool,
121+
hidden: PT.bool,
122+
})
123+
),
124+
popupStrategy: PT.oneOf(["absolute", "fixed"]),
125+
stopClickPropagation: PT.bool,
126+
};
127+
128+
export default ActionsMenu;
129+
130+
/**
131+
* Displays a list of provided action items.
132+
*
133+
* @param {Object} props component properties
134+
* @returns {JSX.Element}
135+
*/
136+
const Menu = ({ items, onClickOutside, referenceElement, strategy }) => {
137+
const [popperElement, setPopperElement] = useState(null);
138+
const [arrowElement, setArrowElement] = useState(null);
139+
const { styles, attributes } = usePopper(referenceElement, popperElement, {
140+
placement: "bottom",
141+
strategy,
142+
modifiers: [
143+
{
144+
name: "flip",
145+
options: {
146+
fallbackPlacements: ["bottom"],
147+
},
148+
},
149+
{
150+
name: "offset",
151+
options: {
152+
// use offset to move the dropdown slightly down
153+
offset: [0, 5],
154+
},
155+
},
156+
{
157+
name: "arrow",
158+
// padding should be equal to border-radius of the dropdown
159+
options: { element: arrowElement, padding: 8 },
160+
},
161+
{
162+
name: "preventOverflow",
163+
options: {
164+
// padding from browser edges
165+
padding: 16,
166+
},
167+
},
168+
{
169+
name: "computeStyles",
170+
options: {
171+
// to fix bug in IE 11 https://github.com/popperjs/popper-core/issues/636
172+
gpuAcceleration: false,
173+
},
174+
},
175+
],
176+
});
177+
178+
useClickOutside(popperElement, onClickOutside, []);
179+
180+
return (
181+
<div
182+
className={compStyles.popover}
183+
ref={setPopperElement}
184+
style={styles.popper}
185+
{...attributes.popper}
186+
>
187+
<div className={compStyles.items}>{items}</div>
188+
<div
189+
ref={setArrowElement}
190+
style={styles.arrow}
191+
className={compStyles.popoverArrow}
192+
/>
193+
</div>
194+
);
195+
};
196+
197+
Menu.propTypes = {
198+
items: PT.array.isRequired,
199+
onClickOutside: PT.func.isRequired,
200+
referenceElement: PT.object,
201+
strategy: PT.oneOf(["absolute", "fixed"]),
202+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
@import "styles/variables";
2+
3+
.container {
4+
position: relative;
5+
display: inline-block;
6+
}
7+
8+
.handle {
9+
display: inline-flex;
10+
align-items: center;
11+
}
12+
13+
.iconArrowDown {
14+
display: inline-block;
15+
width: 12px;
16+
height: 8px;
17+
margin-left: 8px;
18+
}
19+
20+
.handleMenuOpen {
21+
.iconArrowDown {
22+
transform: rotate(180deg);
23+
}
24+
}
25+
26+
.popover {
27+
z-index: 100;
28+
border-radius: 8px;
29+
// min-width: 175px;
30+
background-color: #fff;
31+
box-shadow: 0px 5px 25px #c6c6c6;
32+
}
33+
34+
.popoverArrow {
35+
top: -9px;
36+
border: 10px solid transparent;
37+
border-top: none;
38+
border-bottom-color: #fff;
39+
width: 0;
40+
height: 0;
41+
}
42+
43+
.items {
44+
padding: 16px;
45+
}
46+
47+
.separator {
48+
border-top: 1px solid #e7e7e7;
49+
margin: 5px 0;
50+
}
51+
52+
.item {
53+
padding: 5px 0;
54+
font-size: 12px;
55+
font-weight: bold;
56+
letter-spacing: 0.8px;
57+
text-align: left;
58+
text-transform: uppercase;
59+
white-space: nowrap;
60+
color: $primary-text-color;
61+
cursor: pointer;
62+
}
63+
64+
.danger {
65+
color: #ef476f;
66+
}
67+
68+
.itemDisabled {
69+
color: gray;
70+
opacity: 0.6;
71+
pointer-events: none;
72+
}
73+
74+
.hidden {
75+
display: none;
76+
}

src/components/Button/index.jsx

+5
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import styles from "./styles.module.scss";
1111
* @param {string} [props.className] class name added to root element
1212
* @param {'primary'|'primary-dark'|'primary-light'|'error'|'warning'} [props.color]
1313
* button color
14+
* @param {Object|function} [props.innerRef] Ref object or function to accept the
15+
* ref for <button> element
1416
* @param {boolean} [props.isDisabled] if button is disabled
1517
* @param {boolean} [props.isSelected] if button is selected
1618
* @param {string} [props.name] button name
@@ -26,6 +28,7 @@ const Button = ({
2628
children,
2729
className,
2830
color = "primary",
31+
innerRef,
2932
isDisabled = false,
3033
isSelected = false,
3134
name,
@@ -40,6 +43,7 @@ const Button = ({
4043
data-value={value}
4144
disabled={isDisabled}
4245
name={name || ""}
46+
ref={innerRef}
4347
type={type}
4448
className={cn(
4549
styles.button,
@@ -66,6 +70,7 @@ Button.propTypes = {
6670
"error",
6771
"warning",
6872
]),
73+
innerRef: PT.oneOfType([PT.object, PT.func]),
6974
isDisabled: PT.bool,
7075
isSelected: PT.bool,
7176
name: PT.string,

0 commit comments

Comments
 (0)