@thecb/components 7.0.1-beta.1 → 7.0.2-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thecb/components",
3
- "version": "7.0.1-beta.1",
3
+ "version": "7.0.2-beta.1",
4
4
  "description": "Common lib for CityBase react components",
5
5
  "main": "dist/index.cjs.js",
6
6
  "typings": "dist/index.d.ts",
@@ -0,0 +1,42 @@
1
+ import React from "react";
2
+ import { fallbackValues } from "./Icons.theme";
3
+ import { themeComponent } from "../../../util/themeUtils";
4
+
5
+ const ClipboardIcon = ({ themeValues }) => {
6
+ return (
7
+ <svg
8
+ width="24"
9
+ height="24"
10
+ viewBox="0 0 24 24"
11
+ fill="none"
12
+ xmlns="http://www.w3.org/2000/svg"
13
+ >
14
+ <mask
15
+ id="mask0_1104_623"
16
+ style={{ maskType: "alpha" }}
17
+ maskUnits="userSpaceOnUse"
18
+ x="4"
19
+ y="2"
20
+ width="16"
21
+ height="18"
22
+ >
23
+ <path
24
+ fill-rule="evenodd"
25
+ clip-rule="evenodd"
26
+ d="M9.83929 3.43753H14.1607V5.59825H9.83929L9.83929 3.43753ZM8.75893 3.43753C8.75893 2.84087 9.24262 2.35718 9.83929 2.35718H14.1607C14.7574 2.35718 15.2411 2.84087 15.2411 3.43753V5.59825C15.2411 6.19491 14.7574 6.67861 14.1607 6.67861H9.83929C9.24262 6.67861 8.75893 6.19491 8.75893 5.59825V3.43753ZM6.59821 3.70762H7.94866V5.32816H6.59821C6.29988 5.32816 6.05804 5.57001 6.05804 5.86834V17.4822C6.05804 17.7805 6.29988 18.0224 6.59821 18.0224H17.4018C17.7001 18.0224 17.942 17.7805 17.942 17.4822V5.86834C17.942 5.57001 17.7001 5.32816 17.4018 5.32816H16.0513V3.70762H17.4018C18.5951 3.70762 19.5625 4.67501 19.5625 5.86834V17.4822C19.5625 18.6755 18.5951 19.6429 17.4018 19.6429H6.59821C5.40488 19.6429 4.4375 18.6755 4.4375 17.4822V5.86834C4.4375 4.67501 5.40488 3.70762 6.59821 3.70762Z"
27
+ fill={themeValues.singleIconColor}
28
+ />
29
+ </mask>
30
+ <g mask="url(#mask0_1104_623)">
31
+ <rect width="24" height="24" fill={themeValues.singleIconColor} />
32
+ </g>
33
+ </svg>
34
+ );
35
+ };
36
+
37
+ export default themeComponent(
38
+ ClipboardIcon,
39
+ "Icons",
40
+ fallbackValues,
41
+ "primary"
42
+ );
@@ -0,0 +1,85 @@
1
+ import React, { useState } from "react";
2
+ import ButtonWithAction from "../../atoms/button-with-action";
3
+ import ClipboardIcon from "../../atoms/icons/ClipboardIcon";
4
+ import Popover from "../popover";
5
+ import Stack from "../../atoms/layouts/Stack";
6
+ import Text from "../../atoms/text";
7
+
8
+ /*
9
+ This component will render `text` and a clipboard icon. When hovered, a popover with content `initialPopoverContent` will be displayed.
10
+ When clicked, `text` will be copied to the clipboard, the popover content will change to `copiedPopoverContent` for `copiedPopoverContentDuration` milliseconds,
11
+ and the `onCopy` callback will be executed.
12
+ */
13
+
14
+ const CopyableText = ({ text, onClick }) => {
15
+ return (
16
+ <ButtonWithAction
17
+ data-testid="copyable-trigger"
18
+ contentOverride
19
+ action={onClick}
20
+ variant="ghost"
21
+ extraStyles="padding: 0; margin: 0; min-height: initial; min-width: initial"
22
+ >
23
+ <Stack direction="row" childGap="0.25rem">
24
+ <Text>{text}</Text>
25
+ <ClipboardIcon />
26
+ </Stack>
27
+ </ButtonWithAction>
28
+ );
29
+ };
30
+
31
+ const Copyable = ({
32
+ text,
33
+ onCopy,
34
+ initialPopoverContent = "Click to copy to clipboard",
35
+ copiedPopoverContent = "Copied!",
36
+ withPopover = true,
37
+ popoverMinWidth = "210px",
38
+ copiedPopoverContentDuration = 1000,
39
+ popoverID = 0,
40
+ extraStyles
41
+ }) => {
42
+ const popoverPosition = {
43
+ top: "-65px",
44
+ right: "auto",
45
+ bottom: "auto",
46
+ left: "50%"
47
+ };
48
+ const popoverArrowPosition = {
49
+ arrowTop: "auto",
50
+ arrowRight: "auto",
51
+ arrowBottom: "-8px",
52
+ arrowLeft: "calc(50% - 4px)" // Popover arrow is 8px wide. This places the middle of the arrow in the middle of the popover.
53
+ };
54
+ const [popoverContent, setPopoverContent] = useState(initialPopoverContent);
55
+ const [timer, setTimer] = useState(undefined);
56
+ const onClick = () => {
57
+ setPopoverContent(copiedPopoverContent);
58
+ navigator.clipboard.writeText(text).catch(e => console.error(e));
59
+ onCopy?.();
60
+ if (timer) {
61
+ clearTimeout(timer);
62
+ }
63
+ setTimer(
64
+ setTimeout(() => {
65
+ setPopoverContent(initialPopoverContent);
66
+ setTimer(undefined);
67
+ }, copiedPopoverContentDuration)
68
+ );
69
+ };
70
+ return withPopover ? (
71
+ <Popover
72
+ minWidth={popoverMinWidth}
73
+ position={popoverPosition}
74
+ arrowPosition={popoverArrowPosition}
75
+ popoverID={popoverID}
76
+ extraStyles={`> button#btnPopover${popoverID} { margin: 0 }; #Disclosed${popoverID} { transform: translateX(-50%); }; ${extraStyles}`}
77
+ triggerText={CopyableText({ text, onClick })}
78
+ content={popoverContent}
79
+ ></Popover>
80
+ ) : (
81
+ CopyableText({ text, onClick })
82
+ );
83
+ };
84
+
85
+ export default Copyable;
@@ -0,0 +1,17 @@
1
+ import React from "react";
2
+ import Expand from "../../../util/expand";
3
+
4
+ export interface CopyableProps {
5
+ text: string;
6
+ initialPopoverContent?: string;
7
+ copiedPopoverContent?: string;
8
+ copiedPopoverContentDuration?: number;
9
+ withPopover?: boolean;
10
+ popoverID?: number;
11
+ popoverMinWidth?: string;
12
+ onCopy?: () => void;
13
+ extraStyles?: string;
14
+ }
15
+
16
+ export const Copyable: React.FC<Expand<CopyableProps> &
17
+ React.HTMLAttributes<HTMLElement>>;
@@ -0,0 +1,3 @@
1
+ import Copyable from "./Copyable";
2
+
3
+ export default Copyable;
@@ -1,3 +1,5 @@
1
1
  export * from "./collapsible-section";
2
+ export * from "./copyable";
2
3
  export * from "./editable-table";
3
4
  export * from "./footer-with-subfooter";
5
+ export * from "./popover";
@@ -1,6 +1,7 @@
1
1
  export { default as AddressForm } from "./address-form";
2
2
  export { default as ChangePasswordForm } from "./change-password-form";
3
3
  export { default as CollapsibleSection } from "./collapsible-section";
4
+ export { default as Copyable } from "./copyable";
4
5
  export { default as EditNameForm } from "./edit-name-form";
5
6
  export { default as EditableList } from "./editable-list";
6
7
  export * from "./editable-table";
@@ -1,4 +1,4 @@
1
- import React, { Fragment, useContext } from "react";
1
+ import React, { Fragment, useContext, useRef } from "react";
2
2
  import { ThemeContext } from "styled-components";
3
3
  import AriaModal from "react-aria-modal";
4
4
  import { WHITE, ATHENS_GREY, SILVER_GREY } from "../../../constants/colors";
@@ -47,10 +47,15 @@ const Modal = ({
47
47
  children
48
48
  }) => {
49
49
  const { isMobile } = useContext(ThemeContext);
50
+ const modalContainerRef = useRef(null);
50
51
  return (
51
- <Fragment>
52
+ <div ref={modalContainerRef}>
52
53
  {modalOpen && (
53
54
  <AriaModal
55
+ // fallback to resolve Jest unit test errors when tabbable doesn't exist in jsdom https://github.com/focus-trap/focus-trap-react/issues/91
56
+ focusTrapOptions={{
57
+ fallbackFocus: modalContainerRef?.current
58
+ }}
54
59
  onExit={onExit}
55
60
  getApplicationNode={getApplicationNode}
56
61
  titleText={modalHeaderText}
@@ -188,7 +193,7 @@ const Modal = ({
188
193
  </AriaModal>
189
194
  )}
190
195
  {children}
191
- </Fragment>
196
+ </div>
192
197
  );
193
198
  };
194
199
 
@@ -108,7 +108,7 @@ const Popover = ({
108
108
  right: ${right};
109
109
  bottom: ${bottom};
110
110
  left: ${left};
111
- height: ${height}
111
+ height: ${height};
112
112
  `}
113
113
  >
114
114
  <Paragraph>{content}</Paragraph>
@@ -0,0 +1,26 @@
1
+ import React from "react";
2
+ import Expand from "../../../util/expand";
3
+
4
+ export interface PopoverProps {
5
+ triggerText?: string | JSX.Element;
6
+ content?: string | JSX.Element;
7
+ hasIcon?: boolean;
8
+ iconHelpText?: string; // for screen-readers, required if using an icon for trigger
9
+ popoverID?: number;
10
+ popoverFocus?: boolean;
11
+ extraStyles?: string;
12
+ textExtraStyles?: string;
13
+ minWidth?: string;
14
+ maxWidth?: string;
15
+ height?: string;
16
+ position?: { top: string; right: string; bottom: string; left: string };
17
+ arrowPosition?: {
18
+ arrowTop: string;
19
+ arrowRight: string;
20
+ arrowBottom: string;
21
+ arrowLeft: string;
22
+ };
23
+ }
24
+
25
+ export const Popover: React.FC<Expand<PopoverProps> &
26
+ React.HTMLAttributes<HTMLElement>>;