@telus-uds/components-base 1.93.0 → 1.95.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 (101) hide show
  1. package/CHANGELOG.md +32 -3
  2. package/lib/Autocomplete/Autocomplete.js +2 -1
  3. package/lib/Button/ButtonGroup.js +17 -1
  4. package/lib/Card/Card.js +12 -0
  5. package/lib/Card/CardBase.js +37 -2
  6. package/lib/Carousel/Carousel.js +55 -13
  7. package/lib/Carousel/CarouselItem/CarouselItem.js +86 -12
  8. package/lib/DownloadApp/DownloadApp.js +168 -0
  9. package/lib/DownloadApp/dictionary.js +17 -0
  10. package/lib/DownloadApp/index.js +10 -0
  11. package/lib/Fieldset/FieldsetContainer.js +7 -2
  12. package/lib/Fieldset/FieldsetContainer.native.js +4 -1
  13. package/lib/FileUpload/FileUpload.js +336 -0
  14. package/lib/FileUpload/NotificationContent.js +60 -0
  15. package/lib/FileUpload/dictionary.js +47 -0
  16. package/lib/FileUpload/index.js +10 -0
  17. package/lib/Icon/IconText.js +19 -2
  18. package/lib/Link/LinkBase.js +2 -2
  19. package/lib/Link/TextButton.js +7 -17
  20. package/lib/Modal/Modal.js +1 -1
  21. package/lib/Modal/ModalContent.js +12 -6
  22. package/lib/MultiSelectFilter/ModalOverlay.js +49 -30
  23. package/lib/MultiSelectFilter/MultiSelectFilter.js +170 -131
  24. package/lib/Notification/Notification.js +11 -2
  25. package/lib/Status/Status.js +9 -4
  26. package/lib/TabBar/TabBar.js +133 -0
  27. package/lib/TabBar/TabBarItem.js +184 -0
  28. package/lib/TabBar/index.js +10 -0
  29. package/lib/TextInput/TextInputBase.js +2 -1
  30. package/lib/Tooltip/getTooltipPosition.js +8 -9
  31. package/lib/index.js +24 -0
  32. package/lib/utils/convertFromMegaByteToByte.js +16 -0
  33. package/lib/utils/formatImageSource.js +34 -0
  34. package/lib/utils/index.js +17 -1
  35. package/lib-module/Autocomplete/Autocomplete.js +2 -1
  36. package/lib-module/Button/ButtonGroup.js +17 -1
  37. package/lib-module/Card/Card.js +13 -1
  38. package/lib-module/Card/CardBase.js +38 -3
  39. package/lib-module/Carousel/Carousel.js +55 -13
  40. package/lib-module/Carousel/CarouselItem/CarouselItem.js +86 -12
  41. package/lib-module/DownloadApp/DownloadApp.js +160 -0
  42. package/lib-module/DownloadApp/dictionary.js +10 -0
  43. package/lib-module/DownloadApp/index.js +2 -0
  44. package/lib-module/Fieldset/FieldsetContainer.js +7 -2
  45. package/lib-module/Fieldset/FieldsetContainer.native.js +4 -1
  46. package/lib-module/FileUpload/FileUpload.js +329 -0
  47. package/lib-module/FileUpload/NotificationContent.js +55 -0
  48. package/lib-module/FileUpload/dictionary.js +40 -0
  49. package/lib-module/FileUpload/index.js +2 -0
  50. package/lib-module/Icon/IconText.js +19 -2
  51. package/lib-module/Link/LinkBase.js +2 -2
  52. package/lib-module/Link/TextButton.js +7 -17
  53. package/lib-module/Modal/Modal.js +1 -1
  54. package/lib-module/Modal/ModalContent.js +12 -6
  55. package/lib-module/MultiSelectFilter/ModalOverlay.js +49 -30
  56. package/lib-module/MultiSelectFilter/MultiSelectFilter.js +171 -132
  57. package/lib-module/Notification/Notification.js +11 -2
  58. package/lib-module/Status/Status.js +9 -4
  59. package/lib-module/TabBar/TabBar.js +125 -0
  60. package/lib-module/TabBar/TabBarItem.js +177 -0
  61. package/lib-module/TabBar/index.js +2 -0
  62. package/lib-module/TextInput/TextInputBase.js +2 -1
  63. package/lib-module/Tooltip/getTooltipPosition.js +8 -9
  64. package/lib-module/index.js +3 -0
  65. package/lib-module/utils/convertFromMegaByteToByte.js +10 -0
  66. package/lib-module/utils/formatImageSource.js +27 -0
  67. package/lib-module/utils/index.js +3 -1
  68. package/package.json +4 -3
  69. package/src/Autocomplete/Autocomplete.jsx +2 -1
  70. package/src/Button/ButtonGroup.jsx +9 -1
  71. package/src/Card/Card.jsx +18 -2
  72. package/src/Card/CardBase.jsx +47 -12
  73. package/src/Carousel/Carousel.jsx +59 -13
  74. package/src/Carousel/CarouselItem/CarouselItem.jsx +94 -9
  75. package/src/DownloadApp/DownloadApp.jsx +165 -0
  76. package/src/DownloadApp/dictionary.js +10 -0
  77. package/src/DownloadApp/index.js +3 -0
  78. package/src/Fieldset/FieldsetContainer.jsx +4 -3
  79. package/src/Fieldset/FieldsetContainer.native.jsx +9 -6
  80. package/src/FileUpload/FileUpload.jsx +396 -0
  81. package/src/FileUpload/NotificationContent.jsx +44 -0
  82. package/src/FileUpload/dictionary.js +40 -0
  83. package/src/FileUpload/index.js +3 -0
  84. package/src/Icon/IconText.jsx +21 -4
  85. package/src/Link/LinkBase.jsx +2 -2
  86. package/src/Link/TextButton.jsx +10 -17
  87. package/src/Modal/Modal.jsx +1 -1
  88. package/src/Modal/ModalContent.jsx +8 -3
  89. package/src/MultiSelectFilter/ModalOverlay.jsx +44 -18
  90. package/src/MultiSelectFilter/MultiSelectFilter.jsx +188 -126
  91. package/src/Notification/Notification.jsx +12 -4
  92. package/src/Status/Status.jsx +15 -4
  93. package/src/TabBar/TabBar.jsx +123 -0
  94. package/src/TabBar/TabBarItem.jsx +149 -0
  95. package/src/TabBar/index.js +3 -0
  96. package/src/TextInput/TextInputBase.jsx +1 -1
  97. package/src/Tooltip/getTooltipPosition.js +11 -12
  98. package/src/index.js +3 -0
  99. package/src/utils/convertFromMegaByteToByte.js +11 -0
  100. package/src/utils/formatImageSource.js +29 -0
  101. package/src/utils/index.js +2 -0
@@ -0,0 +1,123 @@
1
+ import React from 'react'
2
+ import PropTypes from 'prop-types'
3
+ import { View, StyleSheet } from 'react-native'
4
+ import TabBarItem from './TabBarItem'
5
+ import { useThemeTokens } from '../ThemeProvider'
6
+ import { variantProp, getTokensPropType, selectSystemProps, a11yProps, viewProps } from '../utils'
7
+
8
+ const selectTabBarContainerStyles = ({
9
+ paddingTop,
10
+ paddingBottom,
11
+ borderTopWidth,
12
+ borderTopColor,
13
+ backgroundColor
14
+ }) => ({
15
+ paddingTop,
16
+ paddingBottom,
17
+ borderTopWidth,
18
+ borderTopColor,
19
+ backgroundColor
20
+ })
21
+
22
+ const selectTabBarItemContainerStyles = ({ paddingLeft, paddingRight, gap }) => ({
23
+ paddingLeft,
24
+ paddingRight,
25
+ gap
26
+ })
27
+
28
+ const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
29
+
30
+ /**
31
+ * TabBar component renders a navigation bar with multiple TabBarItems.
32
+ * It allows users to switch between different views or sections.
33
+ *
34
+ * @component
35
+ * @example
36
+ * const items = [
37
+ * { id: '1', label: 'Home', icon: <HomeIcon />, iconActive: <HomeActiveIcon /> },
38
+ * { id: '2', label: 'Profile', icon: <ProfileIcon />, iconActive: <ProfileActiveIcon /> },
39
+ * ]
40
+ * return (
41
+ * <TabBar
42
+ * items={items}
43
+ * initiallySelectedItem="1"
44
+ * onChange={(itemId) => console.log(itemId)}
45
+ * />
46
+ * )
47
+ */
48
+
49
+ const TabBar = React.forwardRef(
50
+ ({ items = [], initiallySelectedItem = '0', onChange, variant, tokens, ...rest }, ref) => {
51
+ const [isSelected, setIsSelected] = React.useState(initiallySelectedItem)
52
+ const themeTokens = useThemeTokens('TabBar', tokens, variant)
53
+
54
+ const handlePress = (itemId) => {
55
+ setIsSelected(itemId)
56
+ onChange?.(itemId)
57
+ }
58
+
59
+ return (
60
+ <View
61
+ ref={ref}
62
+ style={[styles.tabBar, selectTabBarContainerStyles(themeTokens)]}
63
+ {...selectProps(rest)}
64
+ >
65
+ <View style={[styles.tabBarItem, selectTabBarItemContainerStyles(themeTokens)]}>
66
+ {items.map((item, index) => (
67
+ <TabBarItem
68
+ key={item.id}
69
+ label={item.label}
70
+ href={item.href}
71
+ isSelected={isSelected === item.id}
72
+ icon={item.icon}
73
+ iconActive={item.iconActive}
74
+ onPress={() => handlePress(item.id)}
75
+ id={`tab-item-${index}`}
76
+ accessibilityRole="tablist"
77
+ />
78
+ ))}
79
+ </View>
80
+ </View>
81
+ )
82
+ }
83
+ )
84
+
85
+ TabBar.displayName = 'TabBar'
86
+
87
+ TabBar.propTypes = {
88
+ ...selectedSystemPropTypes,
89
+ /** Array of TabBarItems to be displayed in TabBar */
90
+ items: PropTypes.arrayOf(
91
+ PropTypes.shape({
92
+ id: PropTypes.string.isRequired,
93
+ icon: PropTypes.node,
94
+ iconActive: PropTypes.node,
95
+ label: PropTypes.string.isRequired,
96
+ href: PropTypes.string
97
+ })
98
+ ).isRequired,
99
+ /** Id of the initially selected item. */
100
+ initiallySelectedItem: PropTypes.number,
101
+ /** Callback function to handle item selection change. */
102
+ onChange: PropTypes.func,
103
+ /** Variant of TabBar for styling purposes. */
104
+ variant: variantProp.propType,
105
+ /** Tokens for theming and styling. */
106
+ tokens: getTokensPropType('TabBar')
107
+ }
108
+
109
+ const styles = StyleSheet.create({
110
+ tabBar: {
111
+ flex: 1,
112
+ justifyContent: 'center',
113
+ alignItems: 'center'
114
+ },
115
+ tabBarItem: {
116
+ flex: 1,
117
+ flexDirection: 'row',
118
+ justifyContent: 'space-between',
119
+ width: '100%'
120
+ }
121
+ })
122
+
123
+ export default TabBar
@@ -0,0 +1,149 @@
1
+ import React from 'react'
2
+ import PropTypes from 'prop-types'
3
+ import { Pressable, View, StyleSheet } from 'react-native'
4
+ import { getTokensPropType, resolvePressableTokens, variantProp } from '../utils'
5
+ import { useThemeTokensCallback } from '../ThemeProvider'
6
+ import Typography from '../Typography'
7
+ import Spacer from '../Spacer'
8
+
9
+ const selectTypographyStyles = (
10
+ { fontWeight, color, lineHeight, fontName, activeColor },
11
+ isSelected
12
+ ) => ({
13
+ fontWeight,
14
+ color: isSelected ? activeColor : color,
15
+ lineHeight,
16
+ fontName
17
+ })
18
+
19
+ const selectIconContainerStyles = ({ paddingTop, paddingBottom }) => ({
20
+ paddingTop,
21
+ paddingBottom,
22
+ alignItems: 'center'
23
+ })
24
+
25
+ const selectIconStyles = ({ iconSize, iconColor, activeColor }, isSelected) => ({
26
+ size: iconSize,
27
+ color: isSelected ? activeColor : iconColor
28
+ })
29
+
30
+ const selectContainerStyles = ({ borderRadius, backgroundColor }) => ({
31
+ borderRadius,
32
+ backgroundColor,
33
+ alignSelf: 'center',
34
+ alignItems: 'center'
35
+ })
36
+
37
+ const TabBarItem = React.forwardRef(
38
+ (
39
+ {
40
+ href,
41
+ variant,
42
+ tokens,
43
+ label,
44
+ isSelected,
45
+ id,
46
+ onPress,
47
+ icon: IconComponent,
48
+ iconActive: IconActiveComponent,
49
+ accessibilityRole = 'tab'
50
+ },
51
+ ref
52
+ ) => {
53
+ const getTokens = useThemeTokensCallback('TabBarItem', tokens, variant)
54
+
55
+ const getPressableStyle = ({ pressed, focused, hovered }) => {
56
+ const resolvedTokens = resolvePressableTokens(
57
+ getTokens,
58
+ { pressed, focused, hovered },
59
+ { isSelected }
60
+ )
61
+ return {
62
+ ...resolvedTokens,
63
+ outline: 'none' // removes the default browser :focus outline
64
+ }
65
+ }
66
+
67
+ return (
68
+ <Pressable
69
+ ref={ref}
70
+ href={href}
71
+ nativeID={id}
72
+ onPress={onPress}
73
+ style={({ pressed, focused, hovered }) => [
74
+ styles.flexContainer,
75
+ getPressableStyle({ pressed, focused, hovered })
76
+ ]}
77
+ accessibilityRole={accessibilityRole}
78
+ testID={id}
79
+ >
80
+ {(pressableState) => {
81
+ const resolvedTokens = getPressableStyle(pressableState)
82
+
83
+ return (
84
+ <View style={selectContainerStyles(resolvedTokens)}>
85
+ <View style={[selectIconContainerStyles(resolvedTokens), styles.iconContainer]}>
86
+ {isSelected
87
+ ? IconActiveComponent && (
88
+ <IconActiveComponent {...selectIconStyles(resolvedTokens, isSelected)} />
89
+ )
90
+ : IconComponent && <IconComponent {...selectIconStyles(resolvedTokens)} />}
91
+ </View>
92
+ <Spacer space={1} />
93
+ <Typography
94
+ variant={{ size: 'micro' }}
95
+ tokens={selectTypographyStyles(resolvedTokens, isSelected)}
96
+ align="center"
97
+ >
98
+ {label}
99
+ </Typography>
100
+ <Spacer space={1} />
101
+ </View>
102
+ )
103
+ }}
104
+ </Pressable>
105
+ )
106
+ }
107
+ )
108
+
109
+ TabBarItem.displayName = 'TabBarItem'
110
+
111
+ TabBarItem.propTypes = {
112
+ /** The icon to be displayed when the item is not selected. */
113
+ icon: PropTypes.elementType,
114
+ /** The icon to be displayed when the item is selected. */
115
+ iconActive: PropTypes.elementType,
116
+ /** Tokens for theming and styling the TabBarItem. */
117
+ tokens: getTokensPropType('TabBarItem'),
118
+ /** Variant of the TabBarItem for styling purposes. */
119
+ variant: variantProp.propType,
120
+ /** Callback function to handle press events. */
121
+ onPress: PropTypes.func,
122
+ /** URL to navigate to when the item is pressed. */
123
+ href: PropTypes.string,
124
+ /** Indicates whether the item is selected. */
125
+ isSelected: PropTypes.bool,
126
+ /** Unique identifier for the item. */
127
+ id: PropTypes.string,
128
+ /** Label text for the item. */
129
+ label: PropTypes.string.isRequired,
130
+ /** Accessibility role for the item. */
131
+ accessibilityRole: PropTypes.string
132
+ }
133
+
134
+ const styles = StyleSheet.create({
135
+ flexContainer: {
136
+ flex: 1,
137
+ alignItems: 'center'
138
+ },
139
+ iconContainer: {
140
+ flex: 1,
141
+ justifyContent: 'center',
142
+ alignItems: 'center',
143
+ minWidth: 44,
144
+ minHeight: 40,
145
+ aspectRatio: 1.1
146
+ }
147
+ })
148
+
149
+ export default TabBarItem
@@ -0,0 +1,3 @@
1
+ import TabBar from './TabBar'
2
+
3
+ export default TabBar
@@ -61,7 +61,7 @@ const selectInputStyles = (
61
61
  typeof value === 'number' ? Math.max(0, value - borderWidth) : value
62
62
 
63
63
  const textStyles = applyTextStyles({
64
- fontName,
64
+ fontName: isPassword ? undefined : fontName, // In this case, we don't want to apply the fontName to the input if it's a password because Monotype don't have support for the masked characters in mobile.
65
65
  fontSize,
66
66
  lineHeight,
67
67
  fontWeight,
@@ -1,8 +1,8 @@
1
- function normalizePosition(position) {
1
+ function getAbsolutePosition(position) {
2
2
  const { left, right, bottom, top, width, ...rest } = position
3
3
 
4
4
  // adjust the coordinates so that it fits within the window
5
- const normalized = {
5
+ const finalPosition = {
6
6
  left: Math.max(0, left),
7
7
  right: Math.max(0, right),
8
8
  top: Math.max(0, top),
@@ -12,21 +12,20 @@ function normalizePosition(position) {
12
12
  const getAbsoluteDiff = (value1, value2) => Math.abs(Math.abs(value1) - Math.abs(value2))
13
13
 
14
14
  // adjust the width by whatever has been subtracted from left or right
15
- normalized.width =
15
+ finalPosition.width =
16
16
  width -
17
- Math.abs(getAbsoluteDiff(left, normalized.left) - getAbsoluteDiff(right, normalized.right))
17
+ Math.abs(
18
+ getAbsoluteDiff(left, finalPosition.left) - getAbsoluteDiff(right, finalPosition.right)
19
+ )
18
20
 
19
- if (normalized.top !== top) {
20
- normalized.bottom += normalized.top - top
21
+ if (finalPosition.top !== top) {
22
+ finalPosition.bottom += finalPosition.top - top
21
23
  }
22
24
 
23
- const isNormalized =
24
- normalized.right !== right || normalized.left !== left || normalized.top !== top
25
-
26
25
  return {
27
- ...normalized,
26
+ ...finalPosition,
28
27
  ...rest,
29
- isNormalized
28
+ isNormalized: false
30
29
  }
31
30
  }
32
31
 
@@ -151,7 +150,7 @@ function getTooltipPosition(
151
150
 
152
151
  // prefer 'below' over 'above', since we can always expand the document downwards,
153
152
  // and 'above' might cause issues on small viewports with large tooltips
154
- return normalizePosition(
153
+ return getAbsolutePosition(
155
154
  leastOverflowing.position === 'above'
156
155
  ? findRectByPosition('below', boundingRects)
157
156
  : leastOverflowing
package/src/index.js CHANGED
@@ -8,12 +8,14 @@ export * from './Button'
8
8
  export { default as Card, PressableCardBase } from './Card'
9
9
  export { default as CardGroup } from './CardGroup'
10
10
  export * from './Carousel'
11
+ export { default as FileUpload } from './FileUpload'
11
12
  export { default as Listbox } from './Listbox'
12
13
  export { default as Checkbox } from './Checkbox'
13
14
  export * from './Checkbox'
14
15
  export { default as CheckboxCard } from './CheckboxCard'
15
16
  export { default as CheckboxCardGroup } from './CheckboxCardGroup'
16
17
  export { default as ColourToggle } from './ColourToggle'
18
+ export { default as DownloadApp } from './DownloadApp'
17
19
  export { default as Divider } from './Divider'
18
20
  export { default as ExpandCollapse, Accordion } from './ExpandCollapse'
19
21
  export { default as Feedback } from './Feedback'
@@ -56,6 +58,7 @@ export * from './StackView'
56
58
  export { default as Status } from './Status'
57
59
  export { default as StepTracker } from './StepTracker'
58
60
  export { default as Tabs } from './Tabs'
61
+ export { default as TabBar } from './TabBar'
59
62
  export { default as Tags } from './Tags'
60
63
  export * from './TextInput'
61
64
  export { default as Timeline } from './Timeline'
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Converts a value from megabytes to bytes.
3
+ *
4
+ * @param {number} megaByte - The value in megabytes to be converted.
5
+ * @returns {number} The converted value in bytes.
6
+ */
7
+ const BYTES_IN_A_MEGABYTE = 1024 * 1024
8
+
9
+ export default function convertFromMegaByteToByte(megaByte) {
10
+ return megaByte * BYTES_IN_A_MEGABYTE
11
+ }
@@ -0,0 +1,29 @@
1
+ // react native source for Image and ImageBackground needs proper formatting to work
2
+ // remote sources that start with 'http' and base64 encoded sources that start with 'data:' need be wrapped in uri when passing source prop for Image and ImageBackground ie. source={ uri: image }
3
+ // local source for image can be passed normally without wrapping in uri ie. source={ image }
4
+
5
+ /**
6
+ * example code from react native docs: https://reactnative.dev/docs/imagebackground
7
+ *
8
+ * const image = {uri: 'https://legacy.reactjs.org/logo-og.png'}
9
+ *
10
+ * const App = () => (
11
+ * <View style={styles.container}>
12
+ * <ImageBackground source={image} resizeMode="cover" style={styles.image}>
13
+ * <Text style={styles.text}>Inside</Text>
14
+ * </ImageBackground>
15
+ * </View>
16
+ * )
17
+ */
18
+
19
+ /**
20
+ * Formats the image source for React Native Image and ImageBackground components.
21
+ * @param {string|number} source - The image source, either a URI string or a number (when a local image source is bundled in IOS or Android app).
22
+ * @returns {object|string} - The formatted image source.
23
+ */
24
+ const formatImageSource = (source) =>
25
+ typeof source === 'string' && (source.startsWith('http') || source.startsWith('data:'))
26
+ ? { uri: source }
27
+ : source
28
+
29
+ export default formatImageSource
@@ -21,3 +21,5 @@ export { default as containUniqueFields } from './containUniqueFields'
21
21
  export { default as BaseView } from './BaseView'
22
22
  export { default as htmlAttrs } from './htmlAttrs'
23
23
  export { transformGradient } from './transformGradient'
24
+ export { default as convertFromMegaByteToByte } from './convertFromMegaByteToByte'
25
+ export { default as formatImageSource } from './formatImageSource'