@thecb/components 11.8.0-beta.1 → 11.8.0-beta.10

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": "11.8.0-beta.1",
3
+ "version": "11.8.0-beta.10",
4
4
  "description": "Common lib for CityBase react components",
5
5
  "main": "dist/index.cjs.js",
6
6
  "typings": "dist/index.d.ts",
@@ -81,8 +81,8 @@
81
81
  "vite": "^5.2.11"
82
82
  },
83
83
  "peerDependencies": {
84
- "react": "^16.13.1",
85
- "react-dom": "^16.13.1",
84
+ "react": "^16.13.1 || ^17.0.0",
85
+ "react-dom": "^16.13.1 || ^17.0.0",
86
86
  "react-router-dom": "^6.3.0",
87
87
  "styled-components": "^5.1.1",
88
88
  "styled-theming": "^2.2.0"
@@ -1,8 +1,10 @@
1
1
  import {
2
2
  APRICOT_ORANGE,
3
+ INFO_BLUE,
3
4
  BLUSH_RED,
4
5
  HINT_GREEN,
5
6
  ZEST_ORANGE,
7
+ MATISSE_BLUE,
6
8
  RAZZMATAZZ_RED,
7
9
  EMERALD_GREEN,
8
10
  MUSTARD_YELLOW,
@@ -14,20 +16,18 @@ import {
14
16
  THUNDERBIRD_RED,
15
17
  HAZE_GREEN,
16
18
  ZODIAC_BLUE,
17
- SCIENCE_BLUE,
18
- LUCKY_POINT,
19
- HAWKES_BLUE
19
+ SCIENCE_BLUE
20
20
  } from "../../../constants/colors";
21
21
 
22
22
  const background = {
23
- info: `${HAWKES_BLUE}`,
23
+ info: `${INFO_BLUE}`,
24
24
  warn: `${APRICOT_ORANGE}`,
25
25
  error: `${BLUSH_RED}`,
26
26
  success: `${HINT_GREEN}`
27
27
  };
28
28
 
29
29
  const border = {
30
- info: `${LUCKY_POINT}`,
30
+ info: `${MATISSE_BLUE}`,
31
31
  warn: `${ZEST_ORANGE}`,
32
32
  error: `${RAZZMATAZZ_RED}`,
33
33
  success: `${EMERALD_GREEN}`
@@ -13,7 +13,6 @@ export { default as SortableTableHeading } from "./sortable-table-heading";
13
13
  export { default as CountryDropdown } from "./country-dropdown";
14
14
  export { default as Detail } from "./detail";
15
15
  export { default as DisplayBox } from "./display-box";
16
- export { default as DisplayCard } from "./display-card";
17
16
  export { default as Dropdown } from "./dropdown";
18
17
  export * from "./form-layouts";
19
18
  export { default as FormSelect } from "./form-select";
@@ -31,15 +31,20 @@ const PaymentButtonBar = ({
31
31
  }) => {
32
32
  const { isMobile } = useContext(ThemeContext);
33
33
 
34
+ // cancel URLs are external (back to client system)
35
+ const handleCancel = () => {
36
+ window.location.href = cancelURL;
37
+ };
38
+
34
39
  const backButton =
35
40
  !!cancelURL && !!cancelText ? (
36
- <ButtonWithLink
41
+ <ButtonWithAction
37
42
  text={cancelText}
38
- url={cancelURL}
43
+ action={handleCancel}
39
44
  variant={backButtonVariant}
40
45
  extraStyles={isMobile && "flex-grow: 1"}
41
46
  dataQa={backButtonTestId || cancelText}
42
- aria-labelledby={`${kebabCaseString(cancelText)}-button`}
47
+ aria-label={`${cancelText}, navigate back to previous site`}
43
48
  role="link"
44
49
  />
45
50
  ) : (
@@ -50,7 +55,7 @@ const PaymentButtonBar = ({
50
55
  action={backButtonAction}
51
56
  extraStyles={isMobile && "flex-grow: 1"}
52
57
  dataQa="Back"
53
- aria-labelledby="back-button"
58
+ aria-label={"Back, navigate to previous page"}
54
59
  role="link"
55
60
  />
56
61
  )
@@ -5,24 +5,10 @@ import Paragraph from "../../atoms/paragraph";
5
5
  import { Box } from "../../atoms/layouts";
6
6
  import ButtonWithAction from "../../atoms/button-with-action";
7
7
  import { useOutsideClick } from "../../../hooks";
8
- import { noop } from "../../../util/general";
8
+ import { noop, arrowBorder } from "../../../util/general";
9
9
  import { ESCAPE } from "../../../constants/keyboard";
10
10
  import { fallbackValues } from "./Popover.theme";
11
11
 
12
- const arrowBorder = (borderColor, direction, width = "8px") => {
13
- const angle = `${width} solid transparent`;
14
- const straight = `${width} solid ${borderColor}`;
15
- if (direction == "down") {
16
- return `border-left: ${angle}; border-right: ${angle}; border-top: ${straight}`;
17
- } else if (direction == "up") {
18
- return `border-left: ${angle}; border-right: ${angle}; border-bottom: ${straight}`;
19
- } else if (direction == "left") {
20
- return `border-top: ${angle}; border-bottom: ${angle}; border-right: ${straight}`;
21
- } else if (direction == "right") {
22
- return `border-top: ${angle}; border-bottom: ${angle}; border-left: ${straight}`;
23
- }
24
- };
25
-
26
12
  const Popover = ({
27
13
  themeValues,
28
14
  triggerText = "",
@@ -1,125 +1,184 @@
1
- import React, { useState } from "react";
2
- import { themeComponent } from "../../../util/themeUtils";
1
+ import React, { useContext, useEffect, useRef, useState } from "react";
2
+ import { createThemeValues } from "../../../util/themeUtils";
3
+ import { ThemeContext } from "styled-components";
3
4
  import Text from "../../atoms/text";
4
5
  import Paragraph from "../../atoms/paragraph";
5
6
  import { Box } from "../../atoms/layouts";
6
7
  import ButtonWithAction from "../../atoms/button-with-action";
7
- import { noop } from "../../../util/general";
8
- import { ESCAPE } from "../../../constants/keyboard";
9
- import { fallbackValues } from "./Tooltip.theme";
10
- import IconAdd from "../../atoms/icons/IconAdd";
11
- import { BLACK } from "../../../constants/colors";
8
+ import { noop, arrowBorder } from "../../../util/general";
9
+ import WarningIconXS from "../../atoms/icons/WarningIconXS";
10
+ import {
11
+ MATISSE_BLUE,
12
+ PEACOCK_BLUE,
13
+ SAPPHIRE_BLUE,
14
+ WHITE
15
+ } from "../../../constants/colors";
12
16
 
13
- const arrowBorder = (borderColor, direction, width = "8px") => {
14
- const angle = `${width} solid transparent`;
15
- const straight = `${width} solid ${borderColor}`;
16
- if (direction == "down") {
17
- return `border-left: ${angle}; border-right: ${angle}; border-top: ${straight}`;
18
- } else if (direction == "up") {
19
- return `border-left: ${angle}; border-right: ${angle}; border-bottom: ${straight}`;
20
- } else if (direction == "left") {
21
- return `border-top: ${angle}; border-bottom: ${angle}; border-right: ${straight}`;
22
- } else if (direction == "right") {
23
- return `border-top: ${angle}; border-bottom: ${angle}; border-left: ${straight}`;
24
- }
17
+ const TOOLTIP_THEME_SOURCE = "Popover";
18
+
19
+ const fallbackValues = {
20
+ hoverColor: SAPPHIRE_BLUE,
21
+ activeColor: PEACOCK_BLUE,
22
+ popoverTriggerColor: MATISSE_BLUE,
23
+ borderColor: `rgba(255, 255, 255, 0.85)`
25
24
  };
26
25
 
27
26
  const Tooltip = ({
28
- themeValues,
29
- triggerText = "",
30
- content = "",
27
+ tooltipID,
31
28
  hasIconTrigger = false,
32
- IconTrigger = IconAdd,
33
- iconHelpText = "",
34
- tooltipID = 0,
35
- extraStyles,
36
- textExtraStyles = "",
29
+ IconTrigger = WarningIconXS,
30
+ iconHelpText = "Open the tooltip",
31
+ triggerText = "Open the tooltip",
32
+ tooltipContent = "The contents of the tooltip go here.",
33
+ contentPosition = {
34
+ top: "-110px",
35
+ right: "auto",
36
+ bottom: "auto",
37
+ left: "-225px"
38
+ },
39
+ arrowDirection = "down",
40
+ arrowPosition = {
41
+ arrowTop: "auto",
42
+ arrowRight: "10px",
43
+ arrowBottom: "-8px",
44
+ arrowLeft: "auto"
45
+ },
37
46
  minWidth = "250px",
38
47
  maxWidth = "300px",
39
48
  height = "auto",
40
- position,
41
- arrowPosition,
42
- arrowDirection = "down",
43
- transform = "none",
44
- buttonExtraStyles,
45
- backgroundColor = "white",
46
- borderColor = "rgba(255, 255, 255, 0.85)",
47
- tooltipExtraStyles
49
+ containerExtraStyles = "",
50
+ triggerExtraStyles = "",
51
+ triggerButtonVariant = "smallGhost",
52
+ contentExtraStyles = "",
53
+ contentBackgroundColor = WHITE
48
54
  }) => {
49
- const { hoverColor, activeColor, tooltipTriggerColor } = themeValues;
50
- const { top = "-110px", right = "auto", bottom = "auto", left = "-225px" } =
51
- position ?? {};
55
+ const closeTimeoutRef = useRef(null);
56
+ const [tooltipOpen, setTooltipOpen] = useState(false);
57
+ const themeContext = useContext(ThemeContext);
58
+ const themeValues = createThemeValues(
59
+ themeContext,
60
+ fallbackValues,
61
+ TOOLTIP_THEME_SOURCE
62
+ );
63
+
52
64
  const {
53
- arrowTop = "auto",
54
- arrowRight = "10px",
55
- arrowBottom = "-8px",
56
- arrowLeft = "auto"
57
- } = arrowPosition ?? {};
65
+ borderColor,
66
+ popoverTriggerColor: tooltipTriggerColor,
67
+ hoverColor,
68
+ activeColor
69
+ } = themeValues;
58
70
 
59
- const [tooltipOpen, setTooltipOpen] = useState(false);
71
+ const { top, right, bottom, left } = contentPosition;
72
+ const { arrowTop, arrowRight, arrowBottom, arrowLeft } = arrowPosition;
60
73
 
61
- const handleToggleTooltip = tooltipState => {
62
- if (tooltipOpen !== tooltipState) {
63
- setTooltipOpen(tooltipState);
74
+ const handleToggleTooltip = desiredTooltipState => {
75
+ if (tooltipOpen !== desiredTooltipState) {
76
+ setTooltipOpen(desiredTooltipState);
64
77
  }
65
78
  };
66
79
 
67
80
  const handleKeyboardEvent = e => {
68
- if (e.keyCode === ESCAPE || e.keyCode === 9) {
81
+ if (e.key === "Escape") {
69
82
  handleToggleTooltip(false);
70
83
  }
71
84
  };
72
85
 
86
+ const handleMouseEnter = () => {
87
+ if (closeTimeoutRef.current) {
88
+ clearTimeout(closeTimeoutRef.current);
89
+ closeTimeoutRef.current = null;
90
+ }
91
+ handleToggleTooltip(true);
92
+ };
93
+
94
+ const handleMouseLeave = () => {
95
+ closeTimeoutRef.current = setTimeout(() => {
96
+ handleToggleTooltip(false);
97
+ }, 300);
98
+ };
99
+
100
+ useEffect(() => {
101
+ return () => {
102
+ if (closeTimeoutRef.current) {
103
+ clearTimeout(closeTimeoutRef.current);
104
+ }
105
+ };
106
+ }, []);
107
+
73
108
  return (
74
- <Box padding="0" extraStyles={`position: relative; ${extraStyles}`}>
109
+ <Box
110
+ ref={closeTimeoutRef}
111
+ padding="0"
112
+ extraStyles={`position: relative; ${containerExtraStyles}`}
113
+ onMouseEnter={() => handleMouseEnter(true)}
114
+ onMouseLeave={() => handleMouseLeave(false)}
115
+ data-qa="tooltip-container"
116
+ >
75
117
  <ButtonWithAction
76
- action={() => noop}
118
+ aria-describedby={tooltipID}
119
+ onKeyDown={handleKeyboardEvent}
120
+ variant={triggerButtonVariant}
77
121
  onFocus={() => handleToggleTooltip(true)}
78
122
  onBlur={() => handleToggleTooltip(false)}
79
- onKeyDown={handleKeyboardEvent}
80
123
  onTouchStart={() => handleToggleTooltip(true)}
81
- onTouchEnd={() => handleToggleTooltip(false)}
82
- onMouseEnter={() => handleToggleTooltip(true)}
83
- onMouseLeave={() => handleToggleTooltip(false)}
84
- contentOverride
85
- variant="smallGhost"
86
- tabIndex="0"
87
- aria-describedby={tooltipID}
88
- extraStyles={buttonExtraStyles}
124
+ data-qa="tooltip-trigger"
125
+ contentOverride={true}
89
126
  >
90
- {hasIconTrigger && (
127
+ {hasIconTrigger === true && (
91
128
  <>
92
- <IconTrigger stroke={BLACK} />
129
+ <Box aria-label="Open tooltip">
130
+ <IconTrigger color={tooltipTriggerColor} />
131
+ </Box>
93
132
  <Box padding="0" srOnly>
94
133
  <Text>{iconHelpText}</Text>
95
134
  </Box>
96
135
  </>
97
136
  )}
98
- {!hasIconTrigger && (
99
- <Text extraStyles={textExtraStyles}>{triggerText}</Text>
137
+ {hasIconTrigger === false && (
138
+ <Text
139
+ color={tooltipTriggerColor}
140
+ extraStyles={`
141
+ color: ${tooltipTriggerColor};
142
+ &:visited {
143
+ color: ${tooltipTriggerColor};
144
+ }
145
+ &:hover {
146
+ color: ${hoverColor};
147
+ }
148
+ &:active,
149
+ &:focus {
150
+ color: ${activeColor};
151
+ }
152
+ ${triggerExtraStyles};
153
+ `}
154
+ >
155
+ {triggerText}
156
+ </Text>
100
157
  )}
101
158
  </ButtonWithAction>
102
159
  <Box
103
- background={backgroundColor}
104
- borderRadius="4px"
105
- boxShadow="0px 2px 14px 0px rgb(246, 246, 249), 0px 3px 8px 0px rgb(202, 206, 216)"
106
- id={tooltipID}
107
160
  role="tooltip"
108
- minWidth={minWidth}
109
- maxWidth={maxWidth}
161
+ id={tooltipID}
162
+ aria-hidden={!tooltipOpen}
163
+ background={contentBackgroundColor}
164
+ data-qa="tooltip-contents"
110
165
  extraStyles={`
111
- display: ${tooltipOpen ? "block" : "none"};
112
- position: absolute;
166
+ position: absolute;
167
+ display: ${tooltipOpen ? "block" : "none"};
113
168
  top: ${top};
114
- right: ${right};
115
- bottom: ${bottom};
169
+ right: ${right};
170
+ bottom: ${bottom};
116
171
  left: ${left};
117
172
  height: ${height};
118
- transform: ${transform};
119
- ${tooltipExtraStyles};
173
+ ${contentExtraStyles}
120
174
  `}
175
+ boxShadow={`0px 2px 14px 0px rgb(246, 246, 249), 0px 3px 8px 0px rgb(202, 206, 216)`}
176
+ border={`1px solid transparent`}
177
+ borderRadius="4px"
178
+ minWidth={minWidth}
179
+ maxWidth={maxWidth}
121
180
  >
122
- <Paragraph>{content}</Paragraph>
181
+ <Paragraph>{tooltipContent}</Paragraph>
123
182
  <Box
124
183
  padding="0"
125
184
  extraStyles={`
@@ -140,4 +199,4 @@ const Tooltip = ({
140
199
  );
141
200
  };
142
201
 
143
- export default themeComponent(Tooltip, "Tooltip", fallbackValues);
202
+ export default Tooltip;
@@ -6,7 +6,7 @@ import * as TooltipStories from './Tooltip.stories.js';
6
6
 
7
7
  <Title />
8
8
 
9
- The Tooltip is a fully accessible tooltip widget that displays additional information when a user hovers over or focuses on a specified trigger element. The trigger can either be text supplied using the `triggerText` prop, or a custom Icon component supplied using the `icon` prop. The trigger is rendered as a `ButtonWithAction` with the `smallGhost` variant.
9
+ The Tooltip is a fully accessible tooltip widget that displays additional information when a user hovers over or focuses on a specified trigger element. The trigger can either be text supplied using the `triggerText` prop, or a custom Icon component supplied using the `IconTrigger` prop. The trigger is rendered as a `ButtonWithAction` with the `smallGhost` variant.
10
10
 
11
11
  The Tooltip uses the WAI-ARIA tooltip pattern (`role="tooltip"` and `aria-describedby`) for accessibility. It can be positioned anywhere around the trigger element using the position props. Content and style of the tooltip are customizable.
12
12