@telus-uds/components-base 1.80.1 → 1.82.0

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.
Files changed (74) hide show
  1. package/CHANGELOG.md +30 -2
  2. package/lib/Autocomplete/Autocomplete.js +3 -1
  3. package/lib/Badge/Badge.js +1 -10
  4. package/lib/Fieldset/Fieldset.js +3 -1
  5. package/lib/Fieldset/FieldsetContainer.js +9 -2
  6. package/lib/Fieldset/FieldsetContainer.native.js +7 -1
  7. package/lib/Icon/Icon.js +24 -2
  8. package/lib/Listbox/Listbox.js +7 -1
  9. package/lib/Modal/Modal.js +40 -4
  10. package/lib/Modal/WebModal.js +73 -0
  11. package/lib/PriceLockup/PriceLockup.js +4 -1
  12. package/lib/PriceLockup/utils/renderFootnoteContent.js +2 -2
  13. package/lib/PriceLockup/utils/renderFootnoteLinks.js +2 -2
  14. package/lib/PriceLockup/utils/renderPrice.js +2 -2
  15. package/lib/PriceLockup/utils/renderTypography.js +1 -1
  16. package/lib/ProductCard/ProductCard.js +238 -0
  17. package/lib/ProductCard/dictionary.js +45 -0
  18. package/lib/ProductCard/index.js +10 -0
  19. package/lib/ProductCardGroup/ProductCardGroup.js +79 -0
  20. package/lib/ProductCardGroup/index.js +10 -0
  21. package/lib/TextInput/TextInput.js +1 -0
  22. package/lib/TextInput/TextInputBase.js +4 -1
  23. package/lib/index.js +16 -0
  24. package/lib/utils/ssr-media-query/create-stylesheet/index.js +2 -1
  25. package/lib/utils/ssr-media-query/utils/common.js +21 -2
  26. package/lib-module/Autocomplete/Autocomplete.js +3 -1
  27. package/lib-module/Badge/Badge.js +1 -10
  28. package/lib-module/Fieldset/Fieldset.js +3 -1
  29. package/lib-module/Fieldset/FieldsetContainer.js +7 -2
  30. package/lib-module/Fieldset/FieldsetContainer.native.js +10 -3
  31. package/lib-module/Icon/Icon.js +24 -2
  32. package/lib-module/Listbox/Listbox.js +7 -1
  33. package/lib-module/Modal/Modal.js +42 -5
  34. package/lib-module/Modal/WebModal.js +65 -0
  35. package/lib-module/PriceLockup/PriceLockup.js +4 -1
  36. package/lib-module/PriceLockup/utils/renderFootnoteContent.js +2 -2
  37. package/lib-module/PriceLockup/utils/renderFootnoteLinks.js +2 -2
  38. package/lib-module/PriceLockup/utils/renderPrice.js +2 -2
  39. package/lib-module/PriceLockup/utils/renderTypography.js +1 -1
  40. package/lib-module/ProductCard/ProductCard.js +231 -0
  41. package/lib-module/ProductCard/dictionary.js +38 -0
  42. package/lib-module/ProductCard/index.js +2 -0
  43. package/lib-module/ProductCardGroup/ProductCardGroup.js +69 -0
  44. package/lib-module/ProductCardGroup/index.js +2 -0
  45. package/lib-module/TextInput/TextInput.js +1 -0
  46. package/lib-module/TextInput/TextInputBase.js +4 -1
  47. package/lib-module/index.js +2 -0
  48. package/lib-module/utils/ssr-media-query/create-stylesheet/index.js +3 -2
  49. package/lib-module/utils/ssr-media-query/utils/common.js +19 -1
  50. package/package.json +2 -2
  51. package/src/Autocomplete/Autocomplete.jsx +4 -1
  52. package/src/Badge/Badge.jsx +7 -10
  53. package/src/Fieldset/Fieldset.jsx +3 -1
  54. package/src/Fieldset/FieldsetContainer.jsx +8 -1
  55. package/src/Fieldset/FieldsetContainer.native.jsx +7 -2
  56. package/src/Icon/Icon.jsx +30 -2
  57. package/src/Listbox/Listbox.jsx +6 -1
  58. package/src/Modal/Modal.jsx +42 -3
  59. package/src/Modal/WebModal.jsx +60 -0
  60. package/src/PriceLockup/PriceLockup.jsx +8 -2
  61. package/src/PriceLockup/utils/renderFootnoteContent.jsx +2 -2
  62. package/src/PriceLockup/utils/renderFootnoteLinks.jsx +2 -2
  63. package/src/PriceLockup/utils/renderPrice.jsx +2 -2
  64. package/src/PriceLockup/utils/renderTypography.jsx +1 -1
  65. package/src/ProductCard/ProductCard.jsx +193 -0
  66. package/src/ProductCard/dictionary.js +38 -0
  67. package/src/ProductCard/index.js +3 -0
  68. package/src/ProductCardGroup/ProductCardGroup.jsx +75 -0
  69. package/src/ProductCardGroup/index.js +3 -0
  70. package/src/TextInput/TextInput.jsx +1 -0
  71. package/src/TextInput/TextInputBase.jsx +4 -1
  72. package/src/index.js +2 -0
  73. package/src/utils/ssr-media-query/create-stylesheet/index.js +3 -2
  74. package/src/utils/ssr-media-query/utils/common.js +19 -1
@@ -33,6 +33,8 @@ export { default as Notification } from './Notification';
33
33
  export { default as OrderedList } from './OrderedList';
34
34
  export { default as Pagination } from './Pagination';
35
35
  export { default as PriceLockup } from './PriceLockup';
36
+ export { default as ProductCard } from './ProductCard';
37
+ export { default as ProductCardGroup } from './ProductCardGroup';
36
38
  export { default as Progress } from './Progress';
37
39
  export { default as QuickLinks } from './QuickLinks';
38
40
  export { default as QuickLinksFeature } from './QuickLinksFeature';
@@ -1,7 +1,7 @@
1
1
  import { addCss } from '../utils/inject';
2
2
  import createDeclarationBlock from '../utils/create-declaration-block';
3
3
  import hash from '../hash';
4
- import { isMediaOrPseudo, deepClone, createCssRule } from '../utils/common';
4
+ import { isMediaOrPseudo, deepClone, createCssRule, sanitizeStyle } from '../utils/common';
5
5
  const createStyleSheet = stylesWithQuery => {
6
6
  if (!stylesWithQuery) return {
7
7
  ids: {},
@@ -15,7 +15,8 @@ const createStyleSheet = stylesWithQuery => {
15
15
  const mediaQueriesAndPseudoClasses = Object.keys(stylesWithQuery[key]).filter(isMediaOrPseudo);
16
16
  mediaQueriesAndPseudoClasses.forEach(query => {
17
17
  var _ids;
18
- const css = createDeclarationBlock(stylesWithQuery[key][query]);
18
+ const sanitizedStyle = sanitizeStyle(stylesWithQuery[key][query]);
19
+ const css = createDeclarationBlock(sanitizedStyle);
19
20
  const stringHash = `rnmq-${hash(`${key}${query}${css}`)}`;
20
21
  const rule = createCssRule(query, stringHash, css);
21
22
  addCss(`${stringHash}`, rule);
@@ -12,4 +12,22 @@ const createCssRule = (query, stringHash, css) => {
12
12
  }
13
13
  return rule;
14
14
  };
15
- export { isMedia, isPseudo, isMediaOrPseudo, deepClone, createCssRule };
15
+
16
+ /**
17
+ * Sanitizes the style object by converting any functions to their string representation.
18
+ *
19
+ * @param {Object} style - The style object to sanitize.
20
+ * @returns {Object} - The sanitized style object.
21
+ */
22
+ const sanitizeStyle = style => {
23
+ const sanitizedStyle = {
24
+ ...style
25
+ };
26
+ Object.keys(sanitizedStyle).forEach(property => {
27
+ if (typeof sanitizedStyle[property] === 'function') {
28
+ sanitizedStyle[property] = sanitizedStyle[property].toString();
29
+ }
30
+ });
31
+ return sanitizedStyle;
32
+ };
33
+ export { isMedia, isPseudo, isMediaOrPseudo, deepClone, createCssRule, sanitizeStyle };
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "@floating-ui/react-native": "^0.8.1",
12
12
  "@gorhom/portal": "^1.0.14",
13
13
  "@telus-uds/system-constants": "^1.3.0",
14
- "@telus-uds/system-theme-tokens": "^2.53.0",
14
+ "@telus-uds/system-theme-tokens": "^2.54.0",
15
15
  "airbnb-prop-types": "^2.16.0",
16
16
  "css-mediaquery": "^0.1.2",
17
17
  "expo-linear-gradient": "^12.5.0",
@@ -85,6 +85,6 @@
85
85
  "standard-engine": {
86
86
  "skip": true
87
87
  },
88
- "version": "1.80.1",
88
+ "version": "1.82.0",
89
89
  "types": "types/index.d.ts"
90
90
  }
@@ -217,6 +217,8 @@ const Autocomplete = forwardRef(
217
217
  if (!isControlled && inputRef?.current) inputRef.current.value = newValue
218
218
 
219
219
  if (nested) setNestedSelectedValue(newValue)
220
+
221
+ inputRef.current.focus()
220
222
  }
221
223
 
222
224
  /**
@@ -253,11 +255,12 @@ const Autocomplete = forwardRef(
253
255
  setNestedSelectedValue(null)
254
256
  } else if (
255
257
  event.type === 'keydown' &&
256
- event.key === 'ArrowDown' &&
258
+ (event.key === 'ArrowDown' || event.key === 'Tab') &&
257
259
  isExpanded &&
258
260
  !isLoading &&
259
261
  targetRef?.current
260
262
  ) {
263
+ event.preventDefault()
261
264
  targetRef.current.focus()
262
265
  }
263
266
  }
@@ -1,5 +1,5 @@
1
1
  import React, { forwardRef } from 'react'
2
- import { View, StyleSheet, Platform } from 'react-native'
2
+ import { View, StyleSheet } from 'react-native'
3
3
  import PropTypes from 'prop-types'
4
4
  import { useThemeTokens } from '../ThemeProvider'
5
5
  import Typography from '../Typography'
@@ -38,15 +38,12 @@ const Badge = forwardRef(({ children, tokens, variant = {}, ...rest }, ref) => {
38
38
  return (
39
39
  <View
40
40
  ref={ref}
41
- style={Platform.select({
42
- native: {
43
- ...staticStyles.container,
44
- ...selectContainerBackground(themeTokens),
45
- ...selectContainerBorder(themeTokens),
46
- ...selectContainerPadding(themeTokens)
47
- },
48
- web: {}
49
- })}
41
+ style={[
42
+ staticStyles.container,
43
+ selectContainerBackground(themeTokens),
44
+ selectContainerBorder(themeTokens),
45
+ selectContainerPadding(themeTokens)
46
+ ]}
50
47
  {...selectProps(rest)}
51
48
  >
52
49
  <Typography
@@ -34,7 +34,8 @@ const Fieldset = forwardRef(
34
34
  name: fieldsetName,
35
35
  children,
36
36
  variant = {},
37
- tokens = {}
37
+ tokens = {},
38
+ ...rest
38
39
  },
39
40
  ref
40
41
  ) => {
@@ -118,6 +119,7 @@ const Fieldset = forwardRef(
118
119
  name={fieldsetName}
119
120
  borderStyle={borderStyles(themeTokens)}
120
121
  showBorderStyle={showErrorBorder}
122
+ {...rest}
121
123
  >
122
124
  {stackedContent}
123
125
  </FieldsetContainer>
@@ -3,6 +3,10 @@ import PropTypes from 'prop-types'
3
3
 
4
4
  import cssReset from './cssReset'
5
5
 
6
+ import { selectSystemProps, a11yProps, htmlAttrs } from '../utils'
7
+
8
+ const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, htmlAttrs])
9
+
6
10
  /**
7
11
  * On Web, wraps children with a HTML `<fieldset>` and sets its attributes as necessary.
8
12
  */
@@ -14,7 +18,8 @@ const FieldsetContainer = forwardRef(
14
18
  accessibilityRole,
15
19
  name: fieldsetName,
16
20
  showBorderStyle = false,
17
- borderStyle
21
+ borderStyle,
22
+ ...rest
18
23
  },
19
24
  ref
20
25
  ) => {
@@ -28,6 +33,7 @@ const FieldsetContainer = forwardRef(
28
33
  style={styleContainer}
29
34
  role={accessibilityRole}
30
35
  name={fieldsetName}
36
+ {...selectProps(rest)}
31
37
  >
32
38
  {children}
33
39
  </fieldset>
@@ -37,6 +43,7 @@ const FieldsetContainer = forwardRef(
37
43
  FieldsetContainer.displayName = 'FieldsetContainer'
38
44
 
39
45
  FieldsetContainer.propTypes = {
46
+ ...selectedSystemPropTypes,
40
47
  accessibilityRole: PropTypes.string,
41
48
  children: PropTypes.node,
42
49
  inactive: PropTypes.bool,
@@ -2,16 +2,21 @@ import React, { forwardRef } from 'react'
2
2
  import PropTypes from 'prop-types'
3
3
  import { View } from 'react-native'
4
4
 
5
+ import { selectSystemProps, a11yProps, viewProps } from '../utils'
6
+
7
+ const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
8
+
5
9
  // No equivalent of `<fieldset>` on native, so just apply accessibility role to container.
6
10
  // If a11y testing finds any additional handling is needed at the container level, add it here.
7
- const FieldsetContainer = forwardRef(({ children, accessibilityRole }, ref) => (
8
- <View ref={ref} accessibilityRole={accessibilityRole}>
11
+ const FieldsetContainer = forwardRef(({ children, accessibilityRole, ...rest }, ref) => (
12
+ <View ref={ref} accessibilityRole={accessibilityRole} {...selectProps(rest)}>
9
13
  {children}
10
14
  </View>
11
15
  ))
12
16
  FieldsetContainer.displayName = 'FieldsetContainer'
13
17
 
14
18
  FieldsetContainer.propTypes = {
19
+ ...selectedSystemPropTypes,
15
20
  children: PropTypes.node,
16
21
  accessibilityRole: PropTypes.string
17
22
  }
package/src/Icon/Icon.jsx CHANGED
@@ -26,6 +26,31 @@ const Icon = forwardRef(
26
26
  <IconComponent title={accessibilityLabel} size={size} color={themeTokens.color} />
27
27
  )
28
28
 
29
+ const paddingStyles = variant?.padding
30
+ ? {
31
+ padding: themeTokens.width,
32
+ width: themeTokens.size + themeTokens.width * 2, // sets the diameter of the circle which is the size of the icon plus twice the general padding established to obtain a perfect circle
33
+ height: themeTokens.size + themeTokens.width * 2
34
+ }
35
+ : {}
36
+
37
+ const getIconContentForMobile = () => {
38
+ if (Object.keys(paddingStyles).length) {
39
+ return (
40
+ <View
41
+ style={{
42
+ backgroundColor: themeTokens.backgroundColor,
43
+ borderRadius: themeTokens.borderRadius,
44
+ ...paddingStyles
45
+ }}
46
+ >
47
+ {iconContent}
48
+ </View>
49
+ )
50
+ }
51
+ return iconContent
52
+ }
53
+
29
54
  return Platform.OS === 'web' ? (
30
55
  <View
31
56
  ref={ref}
@@ -41,14 +66,17 @@ const Icon = forwardRef(
41
66
  ]
42
67
  .filter((exists) => exists)
43
68
  .join(' '),
44
- ...style
69
+ ...style,
70
+ backgroundColor: themeTokens.backgroundColor,
71
+ borderRadius: themeTokens.borderRadius,
72
+ ...paddingStyles
45
73
  }}
46
74
  dataSet={dataSet}
47
75
  >
48
76
  {iconContent}
49
77
  </View>
50
78
  ) : (
51
- iconContent
79
+ getIconContentForMobile()
52
80
  )
53
81
  }
54
82
  )
@@ -68,9 +68,14 @@ const Listbox = ({
68
68
  // Return focus to the dropdown control after leaving the last item
69
69
  parentRef?.current?.focus()
70
70
  if (onClose) onClose(event)
71
+ } else if (!nextItemRef && firstItemRef) {
72
+ // If the last item is focused, move the focus to the first one
73
+ event.preventDefault()
74
+ setFocusedIndex(0)
75
+ firstItemRef.current?.focus()
71
76
  }
72
77
  },
73
- [focusedIndex, onClose, parentRef]
78
+ [focusedIndex, onClose, parentRef, firstItemRef]
74
79
  )
75
80
 
76
81
  // Add listeners for mouse clicks outside and for key presses
@@ -1,4 +1,4 @@
1
- import React, { forwardRef } from 'react'
1
+ import React, { forwardRef, useEffect, useRef } from 'react'
2
2
  import {
3
3
  StyleSheet,
4
4
  TouchableWithoutFeedback,
@@ -24,6 +24,7 @@ import IconButton from '../IconButton'
24
24
  import dictionary from './dictionary'
25
25
  import useScrollBlocking from '../utils/useScrollBlocking'
26
26
  import ModalContent from './ModalContent'
27
+ import WebModal from './WebModal'
27
28
 
28
29
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
29
30
 
@@ -138,12 +139,35 @@ const Modal = forwardRef(
138
139
  // Hide the close button if `closeButton` is `null`.
139
140
  const showCloseButton = closeButton !== null
140
141
 
142
+ // These refs are used to manage focus in the web modal container
143
+ const focusTrapRef = useRef(null)
144
+ const closeButtonRef = useRef(null)
145
+
146
+ useEffect(() => {
147
+ if (Platform.OS === 'web') {
148
+ const handleFocus = () => {
149
+ // If the focus is on the last item of the web modal container, move it to the close button
150
+ if (document.activeElement === focusTrapRef.current) {
151
+ closeButtonRef.current.focus()
152
+ }
153
+ return undefined
154
+ }
155
+
156
+ // Add an event listener to manage focus in the web modal container
157
+ document.addEventListener('focusin', handleFocus)
158
+
159
+ // Clean up the event listener
160
+ return () => document.removeEventListener('focusin', handleFocus)
161
+ }
162
+ return undefined
163
+ }, [])
164
+
141
165
  if (!isOpen) {
142
166
  return null
143
167
  }
144
168
 
145
- return (
146
- <NativeModal transparent {...selectProps(rest)}>
169
+ const content = (
170
+ <>
147
171
  <ScrollView contentContainerStyle={[staticStyles.positioningContainer]} ref={modalRef}>
148
172
  <View
149
173
  style={[staticStyles.sizingContainer, selectContainerStyles(themeTokens)]}
@@ -167,6 +191,7 @@ const Modal = forwardRef(
167
191
  icon={CloseIconComponent}
168
192
  accessibilityRole="button"
169
193
  accessibilityLabel={closeLabel}
194
+ ref={closeButtonRef}
170
195
  />
171
196
  )}
172
197
  </View>
@@ -200,6 +225,20 @@ const Modal = forwardRef(
200
225
  <View style={[staticStyles.backdrop, selectBackdropStyles(themeTokens)]} />
201
226
  </TouchableWithoutFeedback>
202
227
  </ScrollView>
228
+ </>
229
+ )
230
+
231
+ if (Platform.OS === 'web') {
232
+ return (
233
+ <WebModal {...selectProps(rest)}>
234
+ {content}
235
+ <View accessibilityRole="button" ref={focusTrapRef} />
236
+ </WebModal>
237
+ )
238
+ }
239
+ return (
240
+ <NativeModal transparent {...selectProps(rest)}>
241
+ {content}
203
242
  </NativeModal>
204
243
  )
205
244
  }
@@ -0,0 +1,60 @@
1
+ import React from 'react'
2
+ import PropTypes from 'prop-types'
3
+ import { View, StyleSheet } from 'react-native'
4
+ import { a11yProps, selectSystemProps, viewProps } from '../utils'
5
+
6
+ const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
7
+
8
+ /**
9
+ * WebModal component.
10
+ *
11
+ * @component
12
+ * @param {Object} props - The component props.
13
+ * @param {ReactNode} props.children - The content of the modal.
14
+ * @returns {JSX.Element} The rendered WebModal component.
15
+ */
16
+ const WebModal = ({ children, ...rest }) => {
17
+ return (
18
+ <View style={staticStyles.container} {...selectProps(rest)}>
19
+ <View style={staticStyles.content}>{children}</View>
20
+ </View>
21
+ )
22
+ }
23
+
24
+ WebModal.propTypes = {
25
+ ...selectedSystemPropTypes,
26
+ // children to be rendered within the modal
27
+ children: PropTypes.oneOfType([PropTypes.node, PropTypes.arrayOf(PropTypes.node)])
28
+ }
29
+
30
+ const staticStyles = StyleSheet.create({
31
+ container: {
32
+ position: 'fixed',
33
+ backgroundColor: 'rgba(0, 0, 0, 0)',
34
+ top: 0,
35
+ right: 0,
36
+ left: 0,
37
+ bottom: 0,
38
+ alignItems: 'stretch',
39
+ boxSizing: 'border-box',
40
+ display: 'flex',
41
+ flexBasis: 'auto',
42
+ flexDirection: 'column',
43
+ flexShrink: 0,
44
+ listStyle: 'none',
45
+ margin: 0,
46
+ minHeight: 0,
47
+ minWidth: 0,
48
+ padding: 0,
49
+ textDecoration: 'none',
50
+ zIndex: 1
51
+ },
52
+ content: {
53
+ flex: 1,
54
+ flexGrow: 1,
55
+ flexShrink: 1,
56
+ flexBasis: 0
57
+ }
58
+ })
59
+
60
+ export default WebModal
@@ -85,7 +85,6 @@ const PriceLockup = ({
85
85
  bottomTextMarginTop,
86
86
  priceMarginBottom,
87
87
  bottomLinksMarginLeft,
88
- topTextMarginBottom,
89
88
  fontColor,
90
89
  dividerColor,
91
90
  ...themeTokens
@@ -104,7 +103,11 @@ const PriceLockup = ({
104
103
 
105
104
  return (
106
105
  <View style={[staticStyles.priceLockupContainer, { ...selectProps(rest) }]}>
107
- {topText ? <View>{renderTypography(topText, topTextTypographyTokens)}</View> : null}
106
+ {topText ? (
107
+ <View style={staticStyles.topText}>
108
+ {renderTypography(topText, topTextTypographyTokens)}
109
+ </View>
110
+ ) : null}
108
111
  {renderPrice(
109
112
  price,
110
113
  rateText,
@@ -214,5 +217,8 @@ export default PriceLockup
214
217
  const staticStyles = StyleSheet.create({
215
218
  priceLockupContainer: {
216
219
  alignSelf: 'flex-start'
220
+ },
221
+ topText: {
222
+ marginBottom: 4
217
223
  }
218
224
  })
@@ -35,7 +35,7 @@ const renderFootnoteContent = (
35
35
  <Text style={selectFootnoteBottomTextContainer({ bottomTextMarginTop })}>
36
36
  {renderTypography(bottomText, bottomTextTypographyTokens, undefined, fontColor)}{' '}
37
37
  </Text>
38
- {footnoteLinks.length <= MAX_FOOTNOTE_LINKS_ALLOWED ? (
38
+ {footnoteLinks?.length <= MAX_FOOTNOTE_LINKS_ALLOWED ? (
39
39
  <View
40
40
  style={[
41
41
  staticStyles.footnoteLinkContainer,
@@ -46,7 +46,7 @@ const renderFootnoteContent = (
46
46
  </View>
47
47
  ) : null}
48
48
  </View>
49
- {footnoteLinks.length > MAX_FOOTNOTE_LINKS_ALLOWED ? (
49
+ {footnoteLinks?.length > MAX_FOOTNOTE_LINKS_ALLOWED ? (
50
50
  <View style={staticStyles.verticalFootnoteLinkContainer}>
51
51
  {renderFootnoteLinks(footnoteLinks, themeTokens, onClickFootnote)}
52
52
  </View>
@@ -14,7 +14,7 @@ const selectFootnoteLinkStyles = (
14
14
  // This is used to apply the proper line height when there is 4 or more footnote links
15
15
  const MAX_FOOTNOTE_LINKS_ALLOWED = 3
16
16
  const lineHeight =
17
- footnoteLinks.length > MAX_FOOTNOTE_LINKS_ALLOWED
17
+ footnoteLinks?.length > MAX_FOOTNOTE_LINKS_ALLOWED
18
18
  ? footnoteLinkFontSize * footnoteLinkLineHeight
19
19
  : undefined
20
20
  return {
@@ -27,7 +27,7 @@ const selectFootnoteLinkStyles = (
27
27
  }
28
28
 
29
29
  const renderFootnoteLinks = (footnoteLinks, themeTokens, onClickFootnote) =>
30
- footnoteLinks && footnoteLinks.length > 0 ? (
30
+ footnoteLinks?.length > 0 ? (
31
31
  <FootnoteLink
32
32
  tokens={selectFootnoteLinkStyles(themeTokens, footnoteLinks)}
33
33
  content={footnoteLinks}
@@ -133,7 +133,7 @@ const renderPrice = (
133
133
  {renderTypography(rateText, rateTypographyTokens, ratePosition, fontColor)}
134
134
  </Text>
135
135
  ) : null}
136
- {!bottomText && footnoteLinks.length <= MAX_FOOTNOTE_LINKS_ALLOWED ? (
136
+ {!bottomText && footnoteLinks?.length <= MAX_FOOTNOTE_LINKS_ALLOWED ? (
137
137
  <Text
138
138
  style={[
139
139
  footnoteLinkPositionStyles,
@@ -144,7 +144,7 @@ const renderPrice = (
144
144
  </Text>
145
145
  ) : null}
146
146
  </View>
147
- {!bottomText && footnoteLinks.length > MAX_FOOTNOTE_LINKS_ALLOWED ? (
147
+ {!bottomText && footnoteLinks?.length > MAX_FOOTNOTE_LINKS_ALLOWED ? (
148
148
  <View style={staticStyles.verticalFootnoteLinkContainer}>
149
149
  {renderFootnoteLinks(footnoteLinks, themeTokens, onClickFootnote)}
150
150
  </View>
@@ -3,7 +3,7 @@ import Typography from '../../Typography'
3
3
 
4
4
  const renderTypography = (value, themeTokens, ratePosition, fontColor) => {
5
5
  const customProps =
6
- ratePosition === 'bottom'
6
+ ratePosition === 'bottom' && value !== '$'
7
7
  ? { variant: { size: 'micro' }, tokens: { color: fontColor } }
8
8
  : { tokens: { ...themeTokens, color: fontColor } }
9
9