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

Modal component, payment cancelling, processing errors in popovers and other improvements #62

Merged
merged 12 commits into from
Jul 5, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
Implemented error messages in popovers for failed working period rows.
  • Loading branch information
MadOPcode committed Jul 3, 2021
commit 4701efd23b43fc0535ed88c2eb4165d6dd1841f9
5 changes: 4 additions & 1 deletion src/components/Checkbox/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import styles from "./styles.module.scss";
* @param {Object} props component properties
* @param {boolean} props.checked whether checkbox is checked
* @param {string} [props.className] class name added to root element
* @param {string} [props.impostorClassName] class name added to checkbox impostor
* @param {boolean} [props.isDisabled] if checkbox is disabled
* @param {string} props.name name for input element
* @param {() => void} props.onChange function called when checkbox changes state
Expand All @@ -21,6 +22,7 @@ import styles from "./styles.module.scss";
const Checkbox = ({
checked,
className,
impostorClassName,
isDisabled = false,
name,
onChange,
Expand All @@ -47,7 +49,7 @@ const Checkbox = ({
checked={checked}
value={option ? option.value : ""}
/>
<span className={styles.impostor} />
<span className={cn(styles.impostor, impostorClassName)} />
{option && option.label && (
<span className={styles.label}>{option.label}</span>
)}
Expand All @@ -57,6 +59,7 @@ const Checkbox = ({
Checkbox.propTypes = {
checked: PT.bool,
className: PT.string,
impostorClassName: PT.string,
isDisabled: PT.bool,
name: PT.string.isRequired,
size: PT.oneOf(["medium", "small"]),
Expand Down
2 changes: 1 addition & 1 deletion src/components/Checkbox/styles.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ input.checkbox {
z-index: 2;
position: relative;
display: inline-block;
vertical-align: -2px;
vertical-align: -3px;
width: 15px;
height: 15px;
line-height: 13px;
Expand Down
20 changes: 20 additions & 0 deletions src/components/Icons/ExclamationMarkCircled/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from "react";
import PT from "prop-types";
import cn from "classnames";
import styles from "./styles.module.scss";

/**
* Displays a white exclamation mark inside red circle.
*
* @param {Object} props component properties
* @returns {JSX.Element}
*/
const ExclamationMarkCircled = (props) => (
<span {...props} className={cn(styles.icon, props.className)} />
);

ExclamationMarkCircled.propTypes = {
className: PT.string,
};

export default ExclamationMarkCircled;
22 changes: 22 additions & 0 deletions src/components/Icons/ExclamationMarkCircled/styles.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
@import "styles/mixins";
@import "styles/variables";

.icon {
display: inline-block;
padding: 2px 0 0;
font-size: 12px;
width: 16px;
height: 16px;
border-radius: 8px;
line-height: 14px;
@include roboto-bold;
text-align: center;
background: $error-color;
color: #fff;
cursor: pointer;

&::before {
content: "!";
display: inline;
}
}
4 changes: 2 additions & 2 deletions src/components/Page/styles.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
display: flex;
flex-direction: column;
@include roboto-regular;
font-size: 14px;
line-height: (22/14);
font-size: $font-size-px;
line-height: ($line-height-px/$font-size-px);
color: $text-color;
background-color: $page-bg-color;

Expand Down
84 changes: 84 additions & 0 deletions src/components/Popover/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import React, { useCallback, useState } from "react";
import PT from "prop-types";
import cn from "classnames";
import Popup from "components/Popup";
import { negate, stopPropagation } from "utils/misc";
import styles from "./styles.module.scss";

/**
* Displays a popover with provided content when clicked on the provided
* target children;
*
* @param {Object} props component properties
* @param {Object} props.children target children
* @param {string} [props.className] class name to be added to root element
* @param {Object} props.content content to show in popover
* @param {string} [props.popupClassName] class name to be added to popup
* @param {boolean} [props.stopClickPropagation] whether to prevent propagation
* of click events on target content
* @param {'absolute'|'fixed'} [props.strategy] popup positioning strategy
* @param {string} [props.targetClassName] class name to be added to wrapper
* element around target children
* @returns {JSX.Element}
*/
const Popover = ({
children,
className,
content,
popupClassName,
stopClickPropagation = false,
strategy = "absolute",
targetClassName,
}) => {
const [isShown, setIsShown] = useState(false);
const [refElem, setRefElem] = useState(null);

const onTargetClick = useCallback(() => {
setIsShown(negate);
}, []);

const onClickOutside = useCallback(() => {
setIsShown(false);
}, []);

return (
<div
className={cn(styles.container, className)}
onClick={stopClickPropagation ? stopPropagation : null}
role="button"
tabIndex={0}
>
<span
className={cn(styles.target, targetClassName)}
onClick={isShown ? null : onTargetClick}
ref={setRefElem}
role="button"
tabIndex={0}
>
{children}
</span>
{!!content && isShown && (
<Popup
className={popupClassName}
onClickOutside={onClickOutside}
referenceElement={refElem}
strategy={strategy}
>
{content}
</Popup>
)}
</div>
);
};

Popover.propTypes = {
children: PT.node,
className: PT.string,
content: PT.node,
popupClassName: PT.string,
stopClickPropagation: PT.bool,
strategy: PT.oneOf(["absolute", "fixed"]),
targetClassName: PT.string,
};

export default Popover;
10 changes: 10 additions & 0 deletions src/components/Popover/styles.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.container {
position: relative;
display: inline-flex;
align-items: baseline;
}

.target {
display: inline-flex;
align-items: baseline;
}
13 changes: 12 additions & 1 deletion src/components/Popup/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { useState } from "react";
import { usePopper } from "react-popper";
import PT from "prop-types";
import cn from "classnames";
import { useClickOutside } from "utils/hooks";
import compStyles from "./styles.module.scss";

/**
Expand All @@ -10,20 +11,26 @@ import compStyles from "./styles.module.scss";
* @param {Object} props component properties
* @param {any} [props.children] child nodes
* @param {string} [props.className] class name to be added to root element
* @param {() => void} [props.onClickOutside] function called when user clicks
* outside the popup
* @param {import('@popperjs/core').Placement} [props.placement] popup placement
* as defined in PopperJS documentation
* @param {Object} props.referenceElement reference element
* @param {'absolute'|'fixed'} [props.strategy] positioning strategy
* @returns {JSX.Element}
*/
const Popup = ({
children,
className,
onClickOutside,
placement = "bottom",
referenceElement,
strategy = "absolute",
}) => {
const [popperElement, setPopperElement] = useState(null);
const [arrowElement, setArrowElement] = useState(null);
const { styles, attributes } = usePopper(referenceElement, popperElement, {
placement: "bottom",
placement,
strategy,
modifiers: [
{ name: "arrow", options: { element: arrowElement, padding: 10 } },
Expand All @@ -32,6 +39,8 @@ const Popup = ({
],
});

useClickOutside(popperElement, onClickOutside, []);

return (
<div
ref={setPopperElement}
Expand All @@ -52,6 +61,8 @@ const Popup = ({
Popup.propTypes = {
children: PT.node,
className: PT.string,
onClickOutside: PT.func,
placement: PT.string,
referenceElement: PT.object.isRequired,
strategy: PT.oneOf(["absolute", "fixed"]),
};
Expand Down
46 changes: 15 additions & 31 deletions src/routes/WorkPeriods/components/PaymentError/index.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import React, { useCallback, useRef, useState } from "react";
import React, { useMemo } from "react";
import PT from "prop-types";
import cn from "classnames";
import Popup from "components/Popup";
import Popover from "components/Popover";
import IconExclamationMark from "components/Icons/ExclamationMarkCircled";
import PaymentErrorDetails from "../PaymentErrorDetails";
import { useClickOutside } from "utils/hooks";
import { negate } from "utils/misc";
import styles from "./styles.module.scss";

/**
Expand All @@ -23,36 +22,21 @@ const PaymentError = ({
isImportant = true,
popupStrategy = "absolute",
}) => {
const [isShowPopup, setIsShowPopup] = useState(false);
const [refElem, setRefElem] = useState(null);
const containerRef = useRef(null);

const onIconClick = useCallback((event) => {
event.stopPropagation();
setIsShowPopup(negate);
}, []);

const onClickOutside = useCallback(() => {
setIsShowPopup(false);
}, []);

useClickOutside(containerRef, onClickOutside, []);

const paymentErrorDetails = useMemo(
() => <PaymentErrorDetails details={errorDetails} />,
[errorDetails]
);
return (
<div className={cn(styles.container, className)} ref={containerRef}>
<span
ref={setRefElem}
<Popover
className={className}
content={paymentErrorDetails}
stopClickPropagation={true}
strategy={popupStrategy}
>
<IconExclamationMark
className={cn(styles.icon, { [styles.isImportant]: isImportant })}
onClick={onIconClick}
role="button"
tabIndex={0}
/>
{isShowPopup && errorDetails && (
<Popup referenceElement={refElem} strategy={popupStrategy}>
<PaymentErrorDetails details={errorDetails} />
</Popup>
)}
</div>
</Popover>
);
};

Expand Down
17 changes: 0 additions & 17 deletions src/routes/WorkPeriods/components/PaymentError/styles.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,9 @@
}

.icon {
display: inline-block;
padding: 2px 0 0;
font-size: 12px;
width: 16px;
height: 16px;
border-radius: 8px;
line-height: 14px;
text-align: center;
background: $error-color;
color: #fff;
opacity: 0.3;
cursor: pointer;

&.isImportant {
opacity: 1;
}

&::before {
content: "!";
display: inline;
font-weight: 700;
}
}
Loading