@widergy/mobile-ui 2.8.1 → 2.9.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/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## [2.9.1](https://github.com/widergy/mobile-ui/compare/v2.9.0...v2.9.1) (2026-03-16)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * [UGGC-78] standardize utbanner and stacked actions utworkflowcontainer ([#488](https://github.com/widergy/mobile-ui/issues/488)) ([ed0772c](https://github.com/widergy/mobile-ui/commit/ed0772c64aaf8818bc3650a284f8f253cd6e33bf))
7
+
8
+ # [2.9.0](https://github.com/widergy/mobile-ui/compare/v2.8.1...v2.9.0) (2026-03-11)
9
+
10
+
11
+ ### Features
12
+
13
+ * add test id ([#487](https://github.com/widergy/mobile-ui/issues/487)) ([c9ea05b](https://github.com/widergy/mobile-ui/commit/c9ea05b22d834c666ad3746f0075657f223c8b78))
14
+
1
15
  ## [2.8.1](https://github.com/widergy/mobile-ui/compare/v2.8.0...v2.8.1) (2026-02-19)
2
16
 
3
17
 
@@ -0,0 +1,5 @@
1
+ export const SIZES = {
2
+ SMALL: 'small',
3
+ MEDIUM: 'medium',
4
+ LARGE: 'large'
5
+ };
@@ -1,33 +1,66 @@
1
1
  import React from 'react';
2
2
  import { View } from 'react-native';
3
- import { bool, shape, string } from 'prop-types';
4
3
 
5
4
  import UTIcon from '../UTIcon';
6
5
  import UTLabel from '../UTLabel';
7
6
  import { useTheme } from '../../theming';
8
- import { mergeMultipleStyles } from '../../utils/styleUtils';
7
+ import { TEST_ID_CONSTANTS } from '../../constants/testIds';
9
8
 
10
- import ownStyles from './styles';
9
+ import { defaultProps, propTypes } from './proptypes';
10
+ import { retrieveStyle } from './theme';
11
11
 
12
- const UTBanner = ({ text, icon, style, withMarkdown }) => {
12
+ const { icon: iconTestId, label: labelTestId } = TEST_ID_CONSTANTS;
13
+
14
+ const UTBanner = ({ text, title, description, icon, size, style, withMarkdown, dataTestId }) => {
13
15
  const theme = useTheme();
14
16
 
15
- const themedStyles = mergeMultipleStyles(ownStyles, theme?.UTBanner, style);
17
+ const displayTitle = title || text; // Backwards compatibility
18
+
19
+ const { bannerStyles, iconStyles, titleStyles, descriptionStyles, textContainerStyles } = retrieveStyle({
20
+ style,
21
+ size,
22
+ theme
23
+ });
16
24
 
17
25
  return (
18
- <View style={themedStyles.banner}>
19
- {icon && <UTIcon name={icon.name} colorTheme={icon.colorTheme} style={themedStyles.icon} />}
20
- <UTLabel withMarkdown={withMarkdown} style={themedStyles.text}>
21
- {text}
22
- </UTLabel>
26
+ <View style={bannerStyles} testID={dataTestId}>
27
+ {icon && (
28
+ <UTIcon
29
+ name={icon.name}
30
+ colorTheme={icon.colorTheme}
31
+ size={iconStyles.size}
32
+ dataTestId={dataTestId ? `${dataTestId}.${iconTestId}` : undefined}
33
+ />
34
+ )}
35
+ <View style={textContainerStyles}>
36
+ {displayTitle && (
37
+ <UTLabel
38
+ withMarkdown={withMarkdown}
39
+ variant={titleStyles.variant}
40
+ weight={titleStyles.weight}
41
+ style={titleStyles.style}
42
+ dataTestId={dataTestId ? `${dataTestId}.title.${labelTestId}` : undefined}
43
+ >
44
+ {displayTitle}
45
+ </UTLabel>
46
+ )}
47
+ {description && (
48
+ <UTLabel
49
+ withMarkdown={withMarkdown}
50
+ variant={descriptionStyles.variant}
51
+ weight={descriptionStyles.weight}
52
+ style={descriptionStyles.style}
53
+ dataTestId={dataTestId ? `${dataTestId}.description.${labelTestId}` : undefined}
54
+ >
55
+ {description}
56
+ </UTLabel>
57
+ )}
58
+ </View>
23
59
  </View>
24
60
  );
25
61
  };
26
62
 
27
- UTBanner.propTypes = {
28
- icon: shape({ name: string, colorTheme: string }),
29
- text: string,
30
- withMarkdown: bool
31
- };
63
+ UTBanner.defaultProps = defaultProps;
64
+ UTBanner.propTypes = propTypes;
32
65
 
33
66
  export default UTBanner;
@@ -0,0 +1,24 @@
1
+ import { bool, object, shape, string } from 'prop-types';
2
+
3
+ import { SIZES } from './constants';
4
+
5
+ export const defaultProps = {
6
+ size: SIZES.MEDIUM
7
+ };
8
+
9
+ export const propTypes = {
10
+ /** @deprecated Use `title` instead. Will be removed in next major version. */
11
+ text: string,
12
+ title: string,
13
+ description: string,
14
+ icon: shape({ name: string, colorTheme: string }),
15
+ size: string,
16
+ withMarkdown: bool,
17
+ dataTestId: string,
18
+ style: shape({
19
+ root: object,
20
+ icon: object,
21
+ title: object,
22
+ description: object
23
+ })
24
+ };
@@ -0,0 +1,102 @@
1
+ import { defaultProps } from './proptypes';
2
+
3
+ const baseBannerTheme = theme => ({
4
+ banner: {
5
+ backgroundColor: theme.Palette.light['03'],
6
+ borderRadius: 8,
7
+ flexDirection: 'row',
8
+ padding: 16,
9
+ gap: 16
10
+ },
11
+ textContainer: {
12
+ flex: 1,
13
+ gap: 8
14
+ }
15
+ });
16
+
17
+ const sizeBannerTheme = size => {
18
+ const definition = {
19
+ large: {
20
+ banner: {
21
+ padding: 24,
22
+ gap: 24
23
+ },
24
+ icon: {
25
+ size: 32
26
+ },
27
+ title: {
28
+ variant: 'title3',
29
+ weight: 'medium'
30
+ }
31
+ },
32
+ medium: {
33
+ banner: {
34
+ padding: 16,
35
+ gap: 16
36
+ },
37
+ icon: {
38
+ size: 24
39
+ },
40
+ title: {
41
+ variant: 'title3',
42
+ weight: 'medium'
43
+ },
44
+ description: {
45
+ variant: 'small'
46
+ }
47
+ },
48
+ small: {
49
+ banner: {
50
+ paddingHorizontal: 12,
51
+ paddingVertical: 8,
52
+ gap: 8
53
+ },
54
+ icon: {
55
+ size: 24
56
+ },
57
+ title: {
58
+ variant: 'title3',
59
+ weight: 'medium'
60
+ },
61
+ description: {
62
+ variant: 'small'
63
+ }
64
+ }
65
+ };
66
+
67
+ return definition[size] || definition[defaultProps.size];
68
+ };
69
+
70
+ export const retrieveStyle = ({ style = {}, size, theme }) => {
71
+ const baseTheme = baseBannerTheme(theme);
72
+ const sizeTheme = sizeBannerTheme(size);
73
+
74
+ const bannerStyles = {
75
+ ...baseTheme.banner,
76
+ ...sizeTheme.banner,
77
+ ...style.banner
78
+ };
79
+
80
+ const iconStyles = {
81
+ size: sizeTheme.icon?.size
82
+ };
83
+
84
+ const titleStyles = {
85
+ variant: sizeTheme.title?.variant,
86
+ weight: sizeTheme.title?.weight,
87
+ style: style.title
88
+ };
89
+
90
+ const descriptionStyles = {
91
+ variant: sizeTheme.description?.variant,
92
+ weight: sizeTheme.description?.weight,
93
+ style: style.description
94
+ };
95
+
96
+ const textContainerStyles = {
97
+ ...baseTheme.textContainer,
98
+ ...sizeTheme.textContainer
99
+ };
100
+
101
+ return { bannerStyles, iconStyles, titleStyles, descriptionStyles, textContainerStyles };
102
+ };
@@ -79,7 +79,7 @@ const UTCheckBox = ({
79
79
  />
80
80
  ) : (
81
81
  <View style={boxStyles}>
82
- <View style={iconContainerStyles}>
82
+ <View testID={`${dataTestId}.checkbox`} style={iconContainerStyles}>
83
83
  <UTIcon
84
84
  colorTheme="light"
85
85
  name={iconName}
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import { View } from 'react-native';
3
3
  import merge from 'lodash/merge';
4
- import { number } from 'prop-types';
4
+ import { bool, number } from 'prop-types';
5
5
 
6
6
  import { TEST_IDS } from '../../../../../../constants/testIds';
7
7
  import UTLabel from '../../../../../UTLabel';
@@ -19,7 +19,15 @@ import { NEXT, RETURN } from './constants';
19
19
 
20
20
  const { workflowContainer } = TEST_IDS;
21
21
 
22
- const BottomActions = ({ bottomSafeArea, message, nextButton, returnButton, summary, style }) => {
22
+ const BottomActions = ({
23
+ bottomSafeArea,
24
+ message,
25
+ nextButton,
26
+ returnButton,
27
+ stackedBottomActions,
28
+ summary,
29
+ style
30
+ }) => {
23
31
  const messageIcon = message?.Icon;
24
32
  const MESSAGE_ICON_SIZE = 16;
25
33
  const checkboxProps = summary?.checkbox;
@@ -28,6 +36,12 @@ const BottomActions = ({ bottomSafeArea, message, nextButton, returnButton, summ
28
36
  const nextButtonEnabled = nextButton && !nextButton.hidden;
29
37
  const returnButtonEnabled = returnButton && !returnButton.hidden;
30
38
 
39
+ const actionButtonStyle = {
40
+ ...themedStyles.returnActionButton,
41
+ ...(nextButtonEnabled &&
42
+ (stackedBottomActions ? themedStyles.actionsChildStacked : themedStyles.actionsChild))
43
+ };
44
+
31
45
  return (
32
46
  <Surface position="top" style={[themedStyles.bottomNav, themedStyles.bottomSafeArea(bottomSafeArea)]}>
33
47
  {summary && (
@@ -92,17 +106,16 @@ const BottomActions = ({ bottomSafeArea, message, nextButton, returnButton, summ
92
106
  {messageIcon && <UTIcon colorTheme="negative" name={messageIcon} size={MESSAGE_ICON_SIZE} />}
93
107
  </View>
94
108
  )}
95
- <View style={themedStyles.actionsContainer}>
109
+ <View
110
+ style={[themedStyles.actionsContainer, stackedBottomActions && themedStyles.actionsContainerStacked]}
111
+ >
96
112
  {returnButtonEnabled && (
97
113
  <ActionButton
98
114
  dataTestId={workflowContainer.bottomActions.returnButton}
99
115
  label={returnButton.label || RETURN}
100
116
  variant="text"
101
117
  style={{
102
- actionButton: {
103
- ...themedStyles.returnActionButton,
104
- ...(nextButtonEnabled ? themedStyles.actionsChild : {})
105
- },
118
+ actionButton: actionButtonStyle,
106
119
  buttonContainer: themedStyles.returnButtonContainer
107
120
  }}
108
121
  {...returnButton}
@@ -129,6 +142,7 @@ BottomActions.propTypes = {
129
142
  message: MessagePropTypes,
130
143
  nextButton: ActionButtonPropTypes,
131
144
  returnButton: ActionButtonPropTypes,
145
+ stackedBottomActions: bool,
132
146
  summary: SummaryPropTypes
133
147
  };
134
148
 
@@ -4,6 +4,9 @@ export default StyleSheet.create({
4
4
  actionsChild: {
5
5
  marginRight: 16
6
6
  },
7
+ actionsChildStacked: {
8
+ marginTop: 8
9
+ },
7
10
  actionsContainer: {
8
11
  alignItems: 'center',
9
12
  flexDirection: 'row',
@@ -12,12 +15,16 @@ export default StyleSheet.create({
12
15
  padding: 16,
13
16
  width: '100%'
14
17
  },
15
- bottomSafeArea: safeArea => ({
16
- paddingBottom: safeArea
17
- }),
18
+ actionsContainerStacked: {
19
+ alignItems: 'stretch',
20
+ flexDirection: 'column-reverse'
21
+ },
18
22
  bottomNav: {
19
23
  backgroundColor: 'white'
20
24
  },
25
+ bottomSafeArea: safeArea => ({
26
+ paddingBottom: safeArea
27
+ }),
21
28
  message: {
22
29
  alignItems: 'center',
23
30
  backgroundColor: 'blue',
@@ -39,6 +39,7 @@ const UTWorkflowContainer = ({
39
39
  scrollable = true,
40
40
  scrollViewRef,
41
41
  stages,
42
+ stackedBottomActions = false,
42
43
  stepsCount,
43
44
  style,
44
45
  subtitle,
@@ -109,6 +110,7 @@ const UTWorkflowContainer = ({
109
110
  message,
110
111
  nextButton,
111
112
  returnButton,
113
+ stackedBottomActions,
112
114
  style: themedStyles,
113
115
  bottomSafeArea: isKeyboardVisible ? 0 : bottomSafeArea,
114
116
  summary
@@ -136,6 +138,7 @@ UTWorkflowContainer.propTypes = {
136
138
  scrollable: bool,
137
139
  scrollViewRef: shape({ current: any }),
138
140
  stages: StagesPropTypes,
141
+ stackedBottomActions: bool,
139
142
  stepsCount: number.isRequired,
140
143
  subtitle: oneOfType([string, element]),
141
144
  summary: SummaryPropTypes,
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@widergy/mobile-ui",
3
3
  "description": "Widergy Mobile Components",
4
4
  "author": "widergy",
5
- "version": "2.8.1",
5
+ "version": "2.9.1",
6
6
  "repository": "https://github.com/widergy/mobile-ui.git",
7
7
  "main": "lib/index.js",
8
8
  "files": [
@@ -1,14 +0,0 @@
1
- import { StyleSheet } from 'react-native';
2
-
3
- export default StyleSheet.create({
4
- banner: {
5
- backgroundColor: '#F4F5F7',
6
- borderRadius: 8,
7
- flexDirection: 'row',
8
- padding: 16,
9
- gap: 16
10
- },
11
- text: {
12
- flex: 1
13
- }
14
- });