@telus-uds/components-base 1.20.0 → 1.22.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 (40) hide show
  1. package/CHANGELOG.md +30 -2
  2. package/__tests17__/ThemeProvider/ThemeProvider.test.jsx +1 -0
  3. package/component-docs.json +461 -3
  4. package/lib/Button/ButtonGroup.js +9 -0
  5. package/lib/ExpandCollapse/Control.js +3 -1
  6. package/lib/Feedback/Feedback.js +3 -1
  7. package/lib/Fieldset/Fieldset.js +42 -13
  8. package/lib/Fieldset/FieldsetContainer.js +9 -3
  9. package/lib/QuickLinksFeature/QuickLinksFeature.js +91 -0
  10. package/lib/QuickLinksFeature/QuickLinksFeatureItem.js +157 -0
  11. package/lib/QuickLinksFeature/index.js +16 -0
  12. package/lib/RadioCard/RadioCardGroup.js +2 -0
  13. package/lib/ThemeProvider/ThemeProvider.js +21 -9
  14. package/lib/ThemeProvider/utils/styles.js +3 -1
  15. package/lib/index.js +18 -0
  16. package/lib-module/Button/ButtonGroup.js +9 -0
  17. package/lib-module/ExpandCollapse/Control.js +3 -1
  18. package/lib-module/Feedback/Feedback.js +3 -1
  19. package/lib-module/Fieldset/Fieldset.js +39 -12
  20. package/lib-module/Fieldset/FieldsetContainer.js +9 -3
  21. package/lib-module/QuickLinksFeature/QuickLinksFeature.js +69 -0
  22. package/lib-module/QuickLinksFeature/QuickLinksFeatureItem.js +130 -0
  23. package/lib-module/QuickLinksFeature/index.js +4 -0
  24. package/lib-module/RadioCard/RadioCardGroup.js +2 -0
  25. package/lib-module/ThemeProvider/ThemeProvider.js +21 -9
  26. package/lib-module/ThemeProvider/utils/styles.js +3 -1
  27. package/lib-module/index.js +2 -0
  28. package/package.json +2 -2
  29. package/src/Button/ButtonGroup.jsx +10 -0
  30. package/src/ExpandCollapse/Control.jsx +3 -2
  31. package/src/Feedback/Feedback.jsx +2 -2
  32. package/src/Fieldset/Fieldset.jsx +40 -13
  33. package/src/Fieldset/FieldsetContainer.jsx +29 -12
  34. package/src/QuickLinksFeature/QuickLinksFeature.jsx +65 -0
  35. package/src/QuickLinksFeature/QuickLinksFeatureItem.jsx +114 -0
  36. package/src/QuickLinksFeature/index.js +6 -0
  37. package/src/RadioCard/RadioCardGroup.jsx +2 -0
  38. package/src/ThemeProvider/ThemeProvider.jsx +19 -6
  39. package/src/ThemeProvider/utils/styles.js +3 -1
  40. package/src/index.js +2 -0
@@ -1,12 +1,12 @@
1
1
  import React, { forwardRef } from 'react';
2
2
  import PropTypes from 'prop-types';
3
+ import StyleSheet from "react-native-web/dist/exports/StyleSheet";
3
4
  import Feedback from '../Feedback';
4
5
  import { spacingProps } from '../utils';
5
6
  import FieldsetContainer from './FieldsetContainer';
6
7
  import { getStackedContent } from '../StackView';
7
8
  import InputLabel from '../InputLabel';
8
9
  import useInputSupports from '../InputSupports/useInputSupports';
9
- import Legend from './Legend';
10
10
  /**
11
11
  * An alternative to InputSupports for groups of input elements that, on web, are to be
12
12
  * wrapped in a `<fieldset>` tag, with label content displated in a `<legend>` tag.
@@ -23,6 +23,8 @@ const Fieldset = /*#__PURE__*/forwardRef((_ref, ref) => {
23
23
  feedback,
24
24
  feedbackPosition = 'top',
25
25
  validation,
26
+ showIcon = false,
27
+ showErrorBorder = false,
26
28
  legend,
27
29
  hint,
28
30
  hintPosition,
@@ -44,21 +46,20 @@ const Fieldset = /*#__PURE__*/forwardRef((_ref, ref) => {
44
46
  validation
45
47
  });
46
48
 
47
- const legendContent = legend && /*#__PURE__*/_jsx(Legend, {
48
- children: /*#__PURE__*/_jsx(InputLabel, {
49
- copy: copy,
50
- label: legend,
51
- hint: hint,
52
- hintPosition: hintPosition,
53
- hintId: hintId,
54
- tooltip: tooltip
55
- })
49
+ const legendContent = legend && /*#__PURE__*/_jsx(InputLabel, {
50
+ copy: copy,
51
+ label: legend,
52
+ hint: hint,
53
+ hintPosition: hintPosition,
54
+ hintId: hintId,
55
+ tooltip: tooltip
56
56
  });
57
57
 
58
58
  const feedbackContent = feedback && /*#__PURE__*/_jsx(Feedback, {
59
59
  id: feedbackId,
60
60
  title: feedback,
61
- validation: validation
61
+ validation: validation,
62
+ icon: showIcon
62
63
  }); // Some accessibility patterns depend on elements being direct children, e.g. fieldset -> legend,
63
64
  // and on MacOS voiceover, radiogroup -> radio. To allow elements to be direct children of the
64
65
  // fieldset as much as possible, but also allow different spacing within content and between
@@ -74,14 +75,30 @@ const Fieldset = /*#__PURE__*/forwardRef((_ref, ref) => {
74
75
  space,
75
76
  preserveFragments: true
76
77
  });
78
+ const borderStyle = validation === 'error' && showErrorBorder && staticStyles.errorBorder || staticStyles.noBorder;
77
79
  return /*#__PURE__*/_jsx(FieldsetContainer, {
78
80
  ref: ref,
79
81
  inactive: inactive,
80
82
  accessibilityRole: accessibilityRole,
81
83
  name: fieldsetName,
84
+ borderStyle: borderStyle,
85
+ showBorderStyle: showErrorBorder,
82
86
  children: stackedContent
83
87
  });
84
88
  });
89
+ const staticStyles = StyleSheet.create({
90
+ errorBorder: {
91
+ border: true,
92
+ borderWidth: 1,
93
+ borderColor: 'red',
94
+ borderRadius: 10,
95
+ padding: 8
96
+ },
97
+ noBorder: {
98
+ padding: 9,
99
+ border: 'unset'
100
+ }
101
+ });
85
102
  Fieldset.displayName = 'Fieldset';
86
103
  Fieldset.propTypes = {
87
104
  children: PropTypes.oneOfType([PropTypes.func, PropTypes.node]),
@@ -148,6 +165,16 @@ Fieldset.propTypes = {
148
165
  /**
149
166
  * Use to visually mark an input as valid or invalid.
150
167
  */
151
- validation: PropTypes.oneOf(['error', 'success'])
168
+ validation: PropTypes.oneOf(['error', 'success']),
169
+
170
+ /**
171
+ * Use to show error or success icon in the feedback
172
+ */
173
+ showIcon: PropTypes.bool,
174
+
175
+ /**
176
+ * Use to show border in case of error for a group of components
177
+ */
178
+ showErrorBorder: PropTypes.bool
152
179
  };
153
180
  export default Fieldset;
@@ -11,12 +11,16 @@ const FieldsetContainer = /*#__PURE__*/forwardRef((_ref, ref) => {
11
11
  children,
12
12
  inactive,
13
13
  accessibilityRole,
14
- name: fieldsetName
14
+ name: fieldsetName,
15
+ showBorderStyle = false,
16
+ borderStyle
15
17
  } = _ref;
18
+ // If needs border for error design or reset the component style
19
+ const styleContainer = showBorderStyle ? borderStyle : cssReset;
16
20
  return /*#__PURE__*/_jsx("fieldset", {
17
21
  ref: ref,
18
22
  disabled: inactive,
19
- style: cssReset,
23
+ style: styleContainer,
20
24
  role: accessibilityRole,
21
25
  name: fieldsetName,
22
26
  children: children
@@ -27,6 +31,8 @@ FieldsetContainer.propTypes = {
27
31
  accessibilityRole: PropTypes.string,
28
32
  children: PropTypes.node,
29
33
  inactive: PropTypes.bool,
30
- name: PropTypes.string
34
+ name: PropTypes.string,
35
+ showBorderStyle: PropTypes.bool,
36
+ borderStyle: PropTypes.object
31
37
  };
32
38
  export default FieldsetContainer;
@@ -0,0 +1,69 @@
1
+ import React, { forwardRef, Children, cloneElement } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { StackWrap } from '../StackView';
4
+ import { useThemeTokens } from '../ThemeProvider';
5
+ import { a11yProps, getTokensPropType, selectSystemProps, variantProp, viewProps } from '../utils'; // pass through and type relevant system props - add more sets for interactive components
6
+
7
+ import { jsx as _jsx } from "react/jsx-runtime";
8
+ const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps]);
9
+
10
+ const isQuickListItem = element => {
11
+ var _element$type, _element$type2;
12
+
13
+ const elementName = (element === null || element === void 0 ? void 0 : (_element$type = element.type) === null || _element$type === void 0 ? void 0 : _element$type.displayName) || (element === null || element === void 0 ? void 0 : (_element$type2 = element.type) === null || _element$type2 === void 0 ? void 0 : _element$type2.name);
14
+ return Boolean(elementName.match(/QuickLinksFeatureItem/));
15
+ };
16
+ /**
17
+ * QuickLinksFeature renders a list of interactive items.
18
+ * - This is the base component that is used as a wrapper and accepts a List of `QuickLinksFeature.Item`
19
+ */
20
+
21
+
22
+ const QuickLinksFeature = /*#__PURE__*/forwardRef((_ref, ref) => {
23
+ let {
24
+ tokens,
25
+ variant,
26
+ tag = 'ul',
27
+ children,
28
+ ...rest
29
+ } = _ref;
30
+ const {
31
+ stackGap,
32
+ stackJustify,
33
+ stackSpace
34
+ } = useThemeTokens('QuickLinksFeature', tokens, variant);
35
+ const items = Children.map(children, child => {
36
+ if (isQuickListItem(child)) {
37
+ return /*#__PURE__*/cloneElement(child, child.props);
38
+ }
39
+
40
+ return null;
41
+ });
42
+ return /*#__PURE__*/_jsx(StackWrap, {
43
+ space: stackSpace,
44
+ gap: stackGap,
45
+ tokens: {
46
+ justifyContent: stackJustify
47
+ },
48
+ tag: tag,
49
+ ref: ref,
50
+ ...selectProps(rest),
51
+ children: items
52
+ });
53
+ });
54
+ QuickLinksFeature.displayName = 'QuickLinksFeature';
55
+ QuickLinksFeature.propTypes = { ...selectedSystemPropTypes,
56
+ tokens: getTokensPropType('QuickLinksFeature'),
57
+ variant: variantProp.propType,
58
+
59
+ /**
60
+ * Default wrapper tag, by default it's "ul"
61
+ */
62
+ tag: PropTypes.string,
63
+
64
+ /**
65
+ * QuickLinksFeature.Item component
66
+ */
67
+ children: PropTypes.node
68
+ };
69
+ export default QuickLinksFeature;
@@ -0,0 +1,130 @@
1
+ import React, { forwardRef, useState } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import Image from "react-native-web/dist/exports/Image";
4
+ import Platform from "react-native-web/dist/exports/Platform";
5
+ import Text from "react-native-web/dist/exports/Text";
6
+ import View from "react-native-web/dist/exports/View";
7
+ import { getTokensPropType, variantProp, withLinkRouter, a11yProps, linkProps, selectSystemProps } from '../utils';
8
+ import { useViewport } from '../ViewportProvider';
9
+ import { useThemeTokensCallback } from '../ThemeProvider';
10
+ import { Link } from '../Link';
11
+ import { StackWrap } from '../StackView'; // pass through and type relevant system props - add more sets for interactive components
12
+
13
+ import { jsx as _jsx } from "react/jsx-runtime";
14
+ import { jsxs as _jsxs } from "react/jsx-runtime";
15
+ const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, linkProps]);
16
+
17
+ const selectImageStyle = imageDimension => ({
18
+ width: imageDimension,
19
+ height: imageDimension,
20
+ ...Platform.select({
21
+ // TODO: https://github.com/telus/universal-design-system/issues/487
22
+ web: {
23
+ transition: 'width 200ms, height 200ms'
24
+ }
25
+ })
26
+ });
27
+
28
+ const selectContainerStyle = _ref => {
29
+ let {
30
+ contentMaxDimension,
31
+ textAlign
32
+ } = _ref;
33
+ return {
34
+ textAlign,
35
+ width: contentMaxDimension,
36
+ overflow: 'hidden'
37
+ };
38
+ };
39
+
40
+ const selectImageContainerStyle = contentMaxDimension => ({
41
+ width: contentMaxDimension,
42
+ height: contentMaxDimension,
43
+ justifyContent: 'center',
44
+ alignItems: 'center'
45
+ });
46
+ /**
47
+ * Component export along with QuickLinksFeature to be used as children
48
+ *
49
+ * It will receive a image source and a accessibility label and will render a link accordingly with the theme tokens
50
+ */
51
+
52
+
53
+ const QuickLinksFeatureItem = /*#__PURE__*/forwardRef((_ref2, ref) => {
54
+ let {
55
+ tokens,
56
+ variant,
57
+ children,
58
+ imageAccessibilityLabel,
59
+ imageSource,
60
+ ...rest
61
+ } = _ref2;
62
+ const viewport = useViewport();
63
+ const getTokens = useThemeTokensCallback('QuickLinksFeatureItem', tokens, variant);
64
+ const [hover, setHover] = useState(false);
65
+ const {
66
+ contentDirection,
67
+ contentSpace,
68
+ contentAlignItems,
69
+ contentMaxDimension,
70
+ imageDimension,
71
+ textAlign
72
+ } = getTokens({
73
+ viewport,
74
+ hover
75
+ });
76
+ return /*#__PURE__*/_jsx(Link, {
77
+ ref: ref,
78
+ tokens: state => {
79
+ setHover(state.hover);
80
+ return getTokens(state);
81
+ },
82
+ ...selectProps(rest),
83
+ children: /*#__PURE__*/_jsx(View, {
84
+ style: selectContainerStyle({
85
+ contentMaxDimension,
86
+ textAlign
87
+ }),
88
+ children: /*#__PURE__*/_jsxs(StackWrap, {
89
+ direction: contentDirection,
90
+ space: contentSpace,
91
+ tokens: {
92
+ alignItems: contentAlignItems
93
+ },
94
+ children: [/*#__PURE__*/_jsx(View, {
95
+ style: selectImageContainerStyle(contentMaxDimension),
96
+ children: /*#__PURE__*/_jsx(Image, {
97
+ accessibilityIgnoresInvertColors: true,
98
+ imageAccessibilityLabel: imageAccessibilityLabel,
99
+ source: imageSource,
100
+ style: selectImageStyle(imageDimension)
101
+ })
102
+ }), /*#__PURE__*/_jsx(Text, {
103
+ children: children
104
+ })]
105
+ })
106
+ })
107
+ });
108
+ });
109
+ QuickLinksFeatureItem.displayName = 'QuickLinksFeatureItem';
110
+ QuickLinksFeatureItem.propTypes = { ...withLinkRouter.propTypes,
111
+ ...selectedSystemPropTypes,
112
+ tokens: getTokensPropType('QuickLinksFeatureItem'),
113
+ variant: variantProp.propType,
114
+
115
+ /**
116
+ * Text which will be rendered within the Link
117
+ */
118
+ children: PropTypes.node.isRequired,
119
+
120
+ /**
121
+ * Image accessibility label
122
+ */
123
+ imageAccessibilityLabel: PropTypes.string.isRequired,
124
+
125
+ /**
126
+ * Image node or Image url
127
+ */
128
+ imageSource: PropTypes.oneOfType([PropTypes.string, PropTypes.node]).isRequired
129
+ };
130
+ export default withLinkRouter(QuickLinksFeatureItem);
@@ -0,0 +1,4 @@
1
+ import QuickLinksFeature from './QuickLinksFeature';
2
+ import QuickLinksFeatureItem from './QuickLinksFeatureItem';
3
+ QuickLinksFeature.Item = QuickLinksFeatureItem;
4
+ export default QuickLinksFeature;
@@ -120,6 +120,8 @@ const RadioCardGroup = /*#__PURE__*/forwardRef((_ref, ref) => {
120
120
  feedback: feedback,
121
121
  inactive: inactive || readOnly,
122
122
  validation: validation,
123
+ showErrorBorder: true,
124
+ showIcon: true,
123
125
  accessibilityRole: "radiogroup",
124
126
  ...selectProps(rest),
125
127
  children: props => /*#__PURE__*/_jsx(StackContainer, {
@@ -5,20 +5,28 @@ import responsiveProps from '../utils/props/responsiveProps';
5
5
  import { jsx as _jsx } from "react/jsx-runtime";
6
6
  export const uninitialisedError = new Error('Theme context used outside of ThemeProvider');
7
7
  export const ThemeContext = /*#__PURE__*/createContext(uninitialisedError);
8
- export const ThemeSetterContext = /*#__PURE__*/createContext(uninitialisedError);
8
+ export const ThemeSetterContext = /*#__PURE__*/createContext(uninitialisedError); // These options default to `true` in v1.x to match legacy defaults and avoid breaking changes.
9
+ // This should change in future major releases to become "opt-in" legacy support.
10
+
11
+ const defaultThemeOptions = {
12
+ // TODO: switch `forceAbsoluteFontSizing` to be false by default in the next major version
13
+ forceAbsoluteFontSizing: true,
14
+ // TODO: switch `forceZIndex` to be false by default in the next major version
15
+ forceZIndex: true,
16
+ // TODO: switch `enableHelmetSSR` to be false by default in the next major version
17
+ enableHelmetSSR: true
18
+ };
9
19
 
10
20
  const ThemeProvider = _ref => {
11
21
  let {
12
22
  children,
13
23
  defaultTheme,
14
- // TODO: switch `forceAbsoluteFontSizing` to be false by default in the next major version
15
- // TODO: switch `forceZIndex` to be false by default in the next major version
16
- themeOptions = {
17
- forceAbsoluteFontSizing: true,
18
- forceZIndex: true
19
- }
24
+ themeOptions = {}
20
25
  } = _ref;
21
- const [theme, setTheme] = useState(defaultTheme); // Validate the theme tokens version on every render.
26
+ const [theme, setTheme] = useState(defaultTheme);
27
+ const appliedThemeOptions = { ...defaultThemeOptions,
28
+ ...themeOptions
29
+ }; // Validate the theme tokens version on every render.
22
30
  // This will intentionally break the application when attempting to use an invalid theme.
23
31
  // This will surface an incompatibility quickly rather than allowing the potential for strange bugs due to missing or incompatible tokens.
24
32
 
@@ -27,7 +35,7 @@ const ThemeProvider = _ref => {
27
35
  value: setTheme,
28
36
  children: /*#__PURE__*/_jsx(ThemeContext.Provider, {
29
37
  value: { ...theme,
30
- themeOptions
38
+ themeOptions: appliedThemeOptions
31
39
  },
32
40
  children: children
33
41
  })
@@ -52,10 +60,14 @@ ThemeProvider.propTypes = {
52
60
  * such as Footnote and Notification to avoid content to stretch width more then the page's width
53
61
  * - `forceZIndex`: available on web only, when set to false, sets zIndex on `View` to be `auto`
54
62
  * and when true, sets zIndex to be `0` (the default from `react-native-web`)
63
+ * - `enableHelmetSSR`: on Web SSR, allows React Helmet to run during server-side rendering. This should be
64
+ * disabled unless a web app has been specifically configured to stop React Helmet accumulating
65
+ * instances (which may cause a memory leak). See React Helmet's docs: https://github.com/nfl/react-helmet
55
66
  */
56
67
  themeOptions: PropTypes.shape({
57
68
  forceAbsoluteFontSizing: PropTypes.bool,
58
69
  forceZIndex: PropTypes.bool,
70
+ enableHelmetSSR: PropTypes.bool,
59
71
  contentMaxWidth: responsiveProps.getTypeOptionallyByViewport(PropTypes.number)
60
72
  })
61
73
  };
@@ -45,7 +45,9 @@ export function applyTextStyles(_ref) {
45
45
  // Don't set undefined font families. May need some validation here that the font is available.
46
46
  // Android doesn't recognise font weights natively so apply custom font weights via `fontFamily`.
47
47
  styles.fontFamily = "".concat(fontName).concat(fontWeight).concat(fontStyle);
48
- } else if (fontWeight) {
48
+ }
49
+
50
+ if (fontWeight) {
49
51
  // If using system default font, apply the font weight directly.
50
52
  // Font weight support in Android is limited to 'bold' or anything else === 'normal'.
51
53
  styles.fontWeight = Platform.OS === 'android' && Number(fontWeight) > 400 ? 'bold' : fontWeight;
@@ -26,6 +26,7 @@ export { default as Notification } from './Notification';
26
26
  export { default as Pagination } from './Pagination';
27
27
  export { default as Progress } from './Progress';
28
28
  export { default as QuickLinks } from './QuickLinks';
29
+ export { default as QuickLinksFeature } from './QuickLinksFeature';
29
30
  export { default as Radio } from './Radio';
30
31
  export * from './Radio';
31
32
  export { default as RadioCard } from './RadioCard';
@@ -50,6 +51,7 @@ export { default as TooltipButton } from './TooltipButton';
50
51
  export { default as Typography } from './Typography';
51
52
  export { default as A11yInfoProvider, useA11yInfo } from './A11yInfoProvider';
52
53
  export { default as BaseProvider } from './BaseProvider';
54
+ export { useHydrationContext } from './BaseProvider/HydrationContext';
53
55
  export { default as ViewportProvider, useViewport, ViewportContext } from './ViewportProvider';
54
56
  export { default as ThemeProvider, useTheme, useSetTheme, useThemeTokens, getThemeTokens, applyOuterBorder, applyTextStyles, applyShadowToken } from './ThemeProvider';
55
57
  export * from './utils';
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "dependencies": {
10
10
  "@gorhom/portal": "^1.0.14",
11
11
  "@telus-uds/system-constants": "^1.2.0",
12
- "@telus-uds/system-theme-tokens": "^2.8.0",
12
+ "@telus-uds/system-theme-tokens": "^2.9.0",
13
13
  "airbnb-prop-types": "^2.16.0",
14
14
  "lodash.debounce": "^4.0.8",
15
15
  "lodash.merge": "^4.6.2",
@@ -70,5 +70,5 @@
70
70
  "standard-engine": {
71
71
  "skip": true
72
72
  },
73
- "version": "1.20.0"
73
+ "version": "1.22.0"
74
74
  }
@@ -50,6 +50,8 @@ const ButtonGroup = forwardRef(
50
50
  feedback,
51
51
  name: inputGroupName,
52
52
  copy,
53
+ iconPosition,
54
+
53
55
  accessibilityRole = maxValues === 1
54
56
  ? 'radiogroup' // radiogroup is cross-platform; only web aria has generic groups
55
57
  : Platform.select({ web: 'group', default: 'none' }),
@@ -123,6 +125,8 @@ const ButtonGroup = forwardRef(
123
125
  toggleOneValue(id, event)
124
126
  }
125
127
 
128
+ const iconProp = itemRest.icon || undefined
129
+
126
130
  const itemA11y = {
127
131
  accessibilityState: { checked: isSelected },
128
132
  accessibilityRole: itemA11yRole,
@@ -141,10 +145,12 @@ const ButtonGroup = forwardRef(
141
145
  tokens={getButtonTokens}
142
146
  selected={isSelected}
143
147
  inactive={inactive}
148
+ icon={iconProp}
144
149
  {...selectItemProps({
145
150
  ...itemRest,
146
151
  ...itemA11y
147
152
  })}
153
+ iconPosition={iconPosition}
148
154
  >
149
155
  {label}
150
156
  </ButtonBase>
@@ -213,6 +219,10 @@ ButtonGroup.propTypes = {
213
219
  * Changing the `initialValues` does not change the user's selections.
214
220
  */
215
221
  initialValues: PropTypes.arrayOf(PropTypes.string),
222
+ /**
223
+ * Defines if the icon will be displayed on the right or left side of the label.
224
+ */
225
+ iconPosition: PropTypes.oneOf(['left', 'right']),
216
226
  /**
217
227
  * Main text used to describe this group, used in Fieldset's Legend element.
218
228
  */
@@ -35,10 +35,11 @@ function selectContainerStyles({
35
35
  }
36
36
 
37
37
  // TODO: use stack / spacer when available
38
- function selectIconContainerStyles({ iconGap, iconPosition }) {
38
+ function selectIconContainerStyles({ iconGap, iconPaddingTop, iconPosition }) {
39
39
  const paddingSide = iconPosition === 'right' ? 'paddingLeft' : 'paddingRight'
40
40
  return {
41
- [paddingSide]: iconGap
41
+ [paddingSide]: iconGap,
42
+ paddingTop: iconPaddingTop
42
43
  }
43
44
  }
44
45
 
@@ -56,8 +56,8 @@ const selectIconContainerStyles = ({ iconGap }) => ({
56
56
  * All accessibility props set on this component will be applied to the outer container.
57
57
  */
58
58
  const Feedback = forwardRef(
59
- ({ title, children, id, validation, tokens, variant, ...rest }, ref) => {
60
- const themeTokens = useThemeTokens('Feedback', tokens, { ...variant, validation })
59
+ ({ title, children, id, validation, tokens, variant, icon, ...rest }, ref) => {
60
+ const themeTokens = useThemeTokens('Feedback', tokens, { ...variant, validation, icon })
61
61
  const { space } = themeTokens
62
62
 
63
63
  const { icon: IconComponent } = themeTokens
@@ -1,5 +1,6 @@
1
1
  import React, { forwardRef } from 'react'
2
2
  import PropTypes from 'prop-types'
3
+ import { StyleSheet } from 'react-native'
3
4
 
4
5
  import Feedback from '../Feedback'
5
6
  import { spacingProps } from '../utils'
@@ -7,7 +8,6 @@ import FieldsetContainer from './FieldsetContainer'
7
8
  import { getStackedContent } from '../StackView'
8
9
  import InputLabel from '../InputLabel'
9
10
  import useInputSupports from '../InputSupports/useInputSupports'
10
- import Legend from './Legend'
11
11
 
12
12
  /**
13
13
  * An alternative to InputSupports for groups of input elements that, on web, are to be
@@ -23,6 +23,8 @@ const Fieldset = forwardRef(
23
23
  feedback,
24
24
  feedbackPosition = 'top',
25
25
  validation,
26
+ showIcon = false,
27
+ showErrorBorder = false,
26
28
  legend,
27
29
  hint,
28
30
  hintPosition,
@@ -43,19 +45,17 @@ const Fieldset = forwardRef(
43
45
  })
44
46
 
45
47
  const legendContent = legend && (
46
- <Legend>
47
- <InputLabel
48
- copy={copy}
49
- label={legend}
50
- hint={hint}
51
- hintPosition={hintPosition}
52
- hintId={hintId}
53
- tooltip={tooltip}
54
- />
55
- </Legend>
48
+ <InputLabel
49
+ copy={copy}
50
+ label={legend}
51
+ hint={hint}
52
+ hintPosition={hintPosition}
53
+ hintId={hintId}
54
+ tooltip={tooltip}
55
+ />
56
56
  )
57
57
  const feedbackContent = feedback && (
58
- <Feedback id={feedbackId} title={feedback} validation={validation} />
58
+ <Feedback id={feedbackId} title={feedback} validation={validation} icon={showIcon} />
59
59
  )
60
60
 
61
61
  // Some accessibility patterns depend on elements being direct children, e.g. fieldset -> legend,
@@ -68,6 +68,9 @@ const Fieldset = forwardRef(
68
68
  ? [legendContent, feedbackContent, childContent]
69
69
  : [legendContent, childContent, feedbackContent]
70
70
  const stackedContent = getStackedContent(orderedContent, { space, preserveFragments: true })
71
+ const borderStyle =
72
+ (validation === 'error' && showErrorBorder && staticStyles.errorBorder) ||
73
+ staticStyles.noBorder
71
74
 
72
75
  return (
73
76
  <FieldsetContainer
@@ -75,12 +78,28 @@ const Fieldset = forwardRef(
75
78
  inactive={inactive}
76
79
  accessibilityRole={accessibilityRole}
77
80
  name={fieldsetName}
81
+ borderStyle={borderStyle}
82
+ showBorderStyle={showErrorBorder}
78
83
  >
79
84
  {stackedContent}
80
85
  </FieldsetContainer>
81
86
  )
82
87
  }
83
88
  )
89
+ const staticStyles = StyleSheet.create({
90
+ errorBorder: {
91
+ border: true,
92
+ borderWidth: 1,
93
+ borderColor: 'red',
94
+ borderRadius: 10,
95
+ padding: 8
96
+ },
97
+ noBorder: {
98
+ padding: 9,
99
+ border: 'unset'
100
+ }
101
+ })
102
+
84
103
  Fieldset.displayName = 'Fieldset'
85
104
 
86
105
  Fieldset.propTypes = {
@@ -136,7 +155,15 @@ Fieldset.propTypes = {
136
155
  /**
137
156
  * Use to visually mark an input as valid or invalid.
138
157
  */
139
- validation: PropTypes.oneOf(['error', 'success'])
158
+ validation: PropTypes.oneOf(['error', 'success']),
159
+ /**
160
+ * Use to show error or success icon in the feedback
161
+ */
162
+ showIcon: PropTypes.bool,
163
+ /**
164
+ * Use to show border in case of error for a group of components
165
+ */
166
+ showErrorBorder: PropTypes.bool
140
167
  }
141
168
 
142
169
  export default Fieldset