@telus-uds/components-base 3.22.0 → 3.23.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 (51) hide show
  1. package/CHANGELOG.md +18 -1
  2. package/lib/cjs/Button/Button.js +2 -0
  3. package/lib/cjs/Button/ButtonBase.js +10 -5
  4. package/lib/cjs/Button/ButtonDropdown.js +2 -0
  5. package/lib/cjs/Button/ButtonGroup.js +45 -38
  6. package/lib/cjs/Button/propTypes.js +6 -0
  7. package/lib/cjs/Carousel/Carousel.js +52 -19
  8. package/lib/cjs/Carousel/CarouselItem/CarouselItem.js +23 -3
  9. package/lib/cjs/Icon/Icon.js +8 -11
  10. package/lib/cjs/Icon/IconText.js +0 -1
  11. package/lib/cjs/Listbox/GroupControl.js +33 -39
  12. package/lib/cjs/Listbox/Listbox.js +22 -13
  13. package/lib/cjs/Listbox/ListboxGroup.js +2 -1
  14. package/lib/cjs/Listbox/ListboxOverlay.js +5 -2
  15. package/lib/cjs/Listbox/PressableItem.js +8 -4
  16. package/lib/cjs/TextInput/TextInputBase.js +5 -1
  17. package/lib/cjs/Validator/Validator.js +171 -135
  18. package/lib/esm/Button/Button.js +2 -0
  19. package/lib/esm/Button/ButtonBase.js +10 -5
  20. package/lib/esm/Button/ButtonDropdown.js +2 -0
  21. package/lib/esm/Button/ButtonGroup.js +44 -39
  22. package/lib/esm/Button/propTypes.js +6 -0
  23. package/lib/esm/Carousel/Carousel.js +52 -19
  24. package/lib/esm/Carousel/CarouselItem/CarouselItem.js +23 -3
  25. package/lib/esm/Icon/Icon.js +8 -11
  26. package/lib/esm/Icon/IconText.js +0 -1
  27. package/lib/esm/Listbox/GroupControl.js +33 -39
  28. package/lib/esm/Listbox/Listbox.js +23 -14
  29. package/lib/esm/Listbox/ListboxGroup.js +2 -1
  30. package/lib/esm/Listbox/ListboxOverlay.js +5 -2
  31. package/lib/esm/Listbox/PressableItem.js +8 -4
  32. package/lib/esm/TextInput/TextInputBase.js +5 -1
  33. package/lib/esm/Validator/Validator.js +171 -135
  34. package/lib/package.json +2 -2
  35. package/package.json +2 -2
  36. package/src/Button/Button.jsx +2 -1
  37. package/src/Button/ButtonBase.jsx +18 -12
  38. package/src/Button/ButtonDropdown.jsx +2 -0
  39. package/src/Button/ButtonGroup.jsx +62 -45
  40. package/src/Button/propTypes.js +6 -0
  41. package/src/Carousel/Carousel.jsx +58 -5
  42. package/src/Carousel/CarouselItem/CarouselItem.jsx +31 -3
  43. package/src/Icon/Icon.jsx +11 -14
  44. package/src/Icon/IconText.jsx +0 -1
  45. package/src/Listbox/GroupControl.jsx +41 -47
  46. package/src/Listbox/Listbox.jsx +26 -9
  47. package/src/Listbox/ListboxGroup.jsx +2 -1
  48. package/src/Listbox/ListboxOverlay.jsx +7 -2
  49. package/src/Listbox/PressableItem.jsx +8 -4
  50. package/src/TextInput/TextInputBase.jsx +5 -1
  51. package/src/Validator/Validator.jsx +180 -159
@@ -1,26 +1,18 @@
1
- import React from 'react';
1
+ import React, { useCallback, useMemo } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import ABBPropTypes from 'airbnb-prop-types';
4
4
  import Platform from "react-native-web/dist/exports/Platform";
5
+ import View from "react-native-web/dist/exports/View";
6
+ import StyleSheet from "react-native-web/dist/exports/StyleSheet";
5
7
  import ButtonBase from './ButtonBase';
6
- import { StackWrap } from '../StackView';
7
8
  import Fieldset from '../Fieldset';
8
9
  import { useViewport } from '../ViewportProvider';
9
10
  import { useThemeTokens, useThemeTokensCallback } from '../ThemeProvider';
10
- import { a11yProps, containUniqueFields, focusHandlerProps, pressProps, getTokensPropType, selectSystemProps, selectTokens, useMultipleInputValues, variantProp, viewProps } from '../utils';
11
+ import { useSpacingScale, a11yProps, containUniqueFields, focusHandlerProps, pressProps, getTokensPropType, selectSystemProps, useMultipleInputValues, variantProp, viewProps } from '../utils';
11
12
  import { getPressHandlersWithArgs } from '../utils/pressability';
12
13
  import { jsx as _jsx } from "react/jsx-runtime";
13
14
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps]);
14
15
  const [selectItemProps, selectedItemPropTypes] = selectSystemProps([a11yProps, focusHandlerProps, pressProps, viewProps]);
15
- const getStackWrapTokens = variant => {
16
- return Platform.select({
17
- web: {
18
- justifyContent: variant?.width === 'equal' ? 'space-evenly' : 'flex-start',
19
- width: variant?.width === 'equal' ? '100%' : 'auto'
20
- },
21
- default: {}
22
- });
23
- };
24
16
  const ButtonGroup = /*#__PURE__*/React.forwardRef((_ref, ref) => {
25
17
  let {
26
18
  variant,
@@ -53,12 +45,6 @@ const ButtonGroup = /*#__PURE__*/React.forwardRef((_ref, ref) => {
53
45
  const themeTokens = useThemeTokens('ButtonGroup', tokens, variant, {
54
46
  viewport
55
47
  });
56
- const themeStackTokens = selectTokens('StackView', themeTokens);
57
- const variantStackTokens = getStackWrapTokens(variant);
58
- const stackTokens = {
59
- ...themeStackTokens,
60
- ...variantStackTokens
61
- };
62
48
  const {
63
49
  direction,
64
50
  space,
@@ -68,15 +54,34 @@ const ButtonGroup = /*#__PURE__*/React.forwardRef((_ref, ref) => {
68
54
  padding,
69
55
  gap
70
56
  } = themeTokens;
57
+ const isMobileNonContained = Platform.OS !== 'web' && (!variant || variant?.style !== 'contained');
71
58
  const themeButtonTokensCallback = useThemeTokensCallback('ButtonGroupItem', tokens, variant);
72
- const getButtonTokens = state => {
59
+ const gapValue = useSpacingScale(gap || space);
60
+ const getButtonTokens = useCallback(state => {
73
61
  const themeButtonTokens = themeButtonTokensCallback(state);
62
+ const shouldUseTransparentBackground = isMobileNonContained && !state.selected && !state.pressed && !state.hover && !state.focus;
74
63
  return {
75
64
  ...themeButtonTokens,
76
- width: variant?.width === 'equal' ? '100%' : 'auto',
77
- flex: variant?.width === 'equal' ? 1 : undefined
65
+ ...(variant?.width === 'equal' && staticStyles.equalWidth),
66
+ ...(shouldUseTransparentBackground && {
67
+ backgroundColor: 'transparent'
68
+ }),
69
+ alignSelf: themeButtonTokens.width ? 'flex-start' : 'center'
78
70
  };
79
- };
71
+ }, [themeButtonTokensCallback, isMobileNonContained, variant?.width]);
72
+ const fieldsetStyles = useMemo(() => ({
73
+ ...staticStyles.fieldsetBase,
74
+ borderRadius,
75
+ backgroundColor: isMobileNonContained ? 'transparent' : backgroundColor || 'transparent',
76
+ padding,
77
+ width: variant?.width === 'equal' ? '100%' : 'auto'
78
+ }), [borderRadius, backgroundColor, padding, variant?.width, isMobileNonContained]);
79
+ const viewStyles = useMemo(() => ({
80
+ ...staticStyles.viewBase,
81
+ flexDirection: direction === 'column' ? 'column' : 'row',
82
+ gap: gapValue || 0,
83
+ justifyContent: variant?.width === 'equal' ? 'space-evenly' : 'flex-start'
84
+ }), [direction, gapValue, variant?.width]);
80
85
  const {
81
86
  currentValues,
82
87
  toggleOneValue
@@ -113,25 +118,11 @@ const ButtonGroup = /*#__PURE__*/React.forwardRef((_ref, ref) => {
113
118
  inactive: inactive,
114
119
  validation: validation,
115
120
  accessibilityRole: accessibilityRole,
116
- style: {
117
- borderRadius,
118
- backgroundColor,
119
- padding,
120
- ...(Platform.OS === 'web' ? {
121
- gap,
122
- width: variant?.width === 'equal' ? '100%' : 'auto'
123
- } : {
124
- alignSelf: 'flex-start'
125
- })
126
- },
121
+ style: fieldsetStyles,
127
122
  ...selectProps(rest),
128
- children: /*#__PURE__*/_jsx(StackWrap, {
123
+ children: /*#__PURE__*/_jsx(View, {
129
124
  accessibilityRole: innerRole,
130
- space: space,
131
- direction: direction,
132
- tokens: stackTokens,
133
- gap: gap,
134
- ref: ref,
125
+ style: viewStyles,
135
126
  children: items.map((_ref2, index) => {
136
127
  let {
137
128
  label,
@@ -291,4 +282,18 @@ ButtonGroup.propTypes = {
291
282
  */
292
283
  copy: PropTypes.oneOf(['en', 'fr'])
293
284
  };
285
+ const staticStyles = StyleSheet.create({
286
+ fieldsetBase: {
287
+ alignSelf: 'flex-start',
288
+ display: 'inline'
289
+ },
290
+ viewBase: {
291
+ flexWrap: 'wrap',
292
+ alignItems: 'center'
293
+ },
294
+ equalWidth: {
295
+ width: '100%',
296
+ flex: 1
297
+ }
298
+ });
294
299
  export default ButtonGroup;
@@ -6,6 +6,12 @@ import { iconComponentPropTypes } from '../Icon';
6
6
  export const textAndA11yText = ABBPropTypes.childrenOf(PropTypes.oneOfType([ABBPropTypes.elementType(A11yText), PropTypes.string]));
7
7
  const buttonPropTypes = {
8
8
  tokens: getTokensPropType('Button'),
9
+ /**
10
+ * If true, the button will honor the align-items value from its parent flex container.
11
+ * If false, the button will always align to 'flex-start' in a flex container.
12
+ * Currently, `heightFull`'s default behaviour will expand to the full height of its parent's flex container. In an upcoming major release, the default behaviour will be changed to expand based on the content. To maintain the expected behaviour, you must explicitly set `heightFull: true`
13
+ */
14
+ heightFull: PropTypes.bool,
9
15
  /**
10
16
  * If true, prevents the button from being pressed, changes the cursor (on web) and accessibility
11
17
  * attributes to communicate this to the user, and applies `inactive: true` appearances from the theme
@@ -357,6 +357,7 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
357
357
  ...rest
358
358
  } = _ref3;
359
359
  let childrenArray = unpackFragment(children);
360
+ const isTransitioningRef = React.useRef(false);
360
361
  const viewport = useViewport();
361
362
  const totalItems = getTotalItems(enableDisplayMultipleItemsPerSlide, childrenArray, viewport);
362
363
  const autoPlayFeatureEnabled = autoPlay && slideDuration > 0 && transitionDuration > 0 && totalItems > 1;
@@ -427,9 +428,14 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
427
428
  setIsAnimating(true);
428
429
  }, [onAnimationStart]);
429
430
  const handleAnimationEnd = React.useCallback(function () {
430
- if (typeof onAnimationEnd === 'function') onAnimationEnd(...arguments);
431
- setIsAnimating(false);
432
- }, [onAnimationEnd]);
431
+ var _ref4;
432
+ const result = (_ref4 = arguments.length - 1, _ref4 < 0 || arguments.length <= _ref4 ? undefined : arguments[_ref4]);
433
+ if (result?.finished) {
434
+ if (typeof onAnimationEnd === 'function') onAnimationEnd(...arguments);
435
+ setIsAnimating(false);
436
+ isTransitioningRef.current = false;
437
+ }
438
+ }, [onAnimationEnd, isTransitioningRef]);
433
439
  const updateOffset = React.useCallback(() => {
434
440
  if (enablePeeking) {
435
441
  const {
@@ -537,6 +543,7 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
537
543
  }
538
544
  const index = activeIndexRef.current + calcDelta;
539
545
  if (skipChanges) {
546
+ isTransitioningRef.current = true;
540
547
  animate(pan, toValue, index);
541
548
  if (enableHero) {
542
549
  animate(heroPan, toValue, index);
@@ -552,6 +559,7 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
552
559
  y: 0
553
560
  };
554
561
  heroToValue.x = heroContainerLayoutRef.current.width * -1 * calcDelta;
562
+ isTransitioningRef.current = true;
555
563
  animate(pan, toValue, index);
556
564
  if (enableHero) {
557
565
  animate(heroPan, heroToValue, index);
@@ -607,29 +615,29 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
607
615
  heroContainerLayoutRef.current = heroContainerLayout;
608
616
  }, [heroContainerLayout]);
609
617
  React.useEffect(() => {
610
- pan.x.addListener(_ref4 => {
618
+ pan.x.addListener(_ref5 => {
611
619
  let {
612
620
  value
613
- } = _ref4;
621
+ } = _ref5;
614
622
  animatedX.current = value;
615
623
  });
616
- pan.y.addListener(_ref5 => {
624
+ pan.y.addListener(_ref6 => {
617
625
  let {
618
626
  value
619
- } = _ref5;
627
+ } = _ref6;
620
628
  animatedY.current = value;
621
629
  });
622
630
  if (enableHero) {
623
- heroPan.x.addListener(_ref6 => {
631
+ heroPan.x.addListener(_ref7 => {
624
632
  let {
625
633
  value
626
- } = _ref6;
634
+ } = _ref7;
627
635
  heroAnimatedX.current = value;
628
636
  });
629
- heroPan.y.addListener(_ref7 => {
637
+ heroPan.y.addListener(_ref8 => {
630
638
  let {
631
639
  value
632
- } = _ref7;
640
+ } = _ref8;
633
641
  heroAnimatedY.current = value;
634
642
  });
635
643
  }
@@ -666,7 +674,7 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
666
674
  stopAutoplay();
667
675
  };
668
676
  }, [stopAutoplay]);
669
- const onContainerLayout = _ref8 => {
677
+ const onContainerLayout = _ref9 => {
670
678
  let {
671
679
  nativeEvent: {
672
680
  layout: {
@@ -676,7 +684,7 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
676
684
  height
677
685
  }
678
686
  }
679
- } = _ref8;
687
+ } = _ref9;
680
688
  return setContainerLayout(prevState => ({
681
689
  ...prevState,
682
690
  x,
@@ -685,7 +693,7 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
685
693
  height
686
694
  }));
687
695
  };
688
- const onHeroContainerLayout = _ref9 => {
696
+ const onHeroContainerLayout = _ref10 => {
689
697
  let {
690
698
  nativeEvent: {
691
699
  layout: {
@@ -695,7 +703,7 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
695
703
  height
696
704
  }
697
705
  }
698
- } = _ref9;
706
+ } = _ref10;
699
707
  return setHeroContainerLayout(prevState => ({
700
708
  ...prevState,
701
709
  x,
@@ -704,14 +712,14 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
704
712
  height
705
713
  }));
706
714
  };
707
- const onPreviousNextNavigationButtonLayout = _ref10 => {
715
+ const onPreviousNextNavigationButtonLayout = _ref11 => {
708
716
  let {
709
717
  nativeEvent: {
710
718
  layout: {
711
719
  width
712
720
  }
713
721
  }
714
- } = _ref10;
722
+ } = _ref11;
715
723
  return setPreviousNextNavigationButtonWidth(width);
716
724
  };
717
725
  const isSwipeAllowed = React.useCallback(() => {
@@ -873,6 +881,11 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
873
881
  }
874
882
  setisCarouselPlaying(prevState => !prevState);
875
883
  }, [isCarouselPlaying, stopAutoplay, startAutoplay]);
884
+ const handleKeyDown = React.useCallback(event => {
885
+ if (isTransitioningRef.current && event.key === 'Tab') {
886
+ event.preventDefault();
887
+ }
888
+ }, [isTransitioningRef]);
876
889
  return /*#__PURE__*/_jsxs(View, {
877
890
  style: selectRootContainerStyles(enableHero, viewport),
878
891
  children: [/*#__PURE__*/_jsx(View, {
@@ -898,6 +911,9 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
898
911
  ref: ref,
899
912
  ...systemProps,
900
913
  ...containerProps,
914
+ ...(Platform.OS === 'web' ? {
915
+ onKeyDown: handleKeyDown
916
+ } : {}),
901
917
  children: [isAutoPlayEnabled ? /*#__PURE__*/_jsx(View, {
902
918
  style: [staticStyles.animationControlButton, selectControlButtonPositionStyles({
903
919
  positionVariant: previousNextNavigationPosition,
@@ -952,10 +968,27 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
952
968
  // This is a known Voiceover bug: https://github.com/phetsims/a11y-research/issues/132
953
969
  accessibilityLiveRegion: accessibilityLiveRegion,
954
970
  children: childrenArray.map((element, index) => {
955
- const hidden = !isAnimating && index !== activeIndex;
971
+ let hidden = !isAnimating && index !== activeIndex;
972
+ if (enablePeeking && !isAnimating) {
973
+ if (enableDisplayMultipleItemsPerSlide) {
974
+ const maxItemsForSlide = getMaximumItemsForSlide(enableDisplayMultipleItemsPerSlide, viewport);
975
+ if (index >= activeIndex * maxItemsForSlide - 1 && index < activeIndex * maxItemsForSlide + maxItemsForSlide + 1) {
976
+ hidden = false;
977
+ } else {
978
+ hidden = true;
979
+ }
980
+ } else if (index >= activeIndex - 1 && index <= activeIndex + 1) {
981
+ hidden = false;
982
+ }
983
+ } else if (!enablePeeking && enableDisplayMultipleItemsPerSlide && !isAnimating) {
984
+ const maxItemsForSlide = getMaximumItemsForSlide(enableDisplayMultipleItemsPerSlide, viewport);
985
+ if (index >= activeIndex * maxItemsForSlide && index < activeIndex * maxItemsForSlide + maxItemsForSlide) {
986
+ hidden = false;
987
+ }
988
+ }
956
989
  const clonedElement = /*#__PURE__*/React.cloneElement(element, {
957
990
  elementIndex: index,
958
- hidden: enablePeeking || enableDisplayMultipleItemsPerSlide ? false : hidden,
991
+ hidden,
959
992
  enablePeeking,
960
993
  peekingProps: getPeekingProps(viewport),
961
994
  enableDisplayMultipleItemsPerSlide,
@@ -97,13 +97,33 @@ const CarouselItem = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
97
97
  });
98
98
  const focusabilityProps = activeIndex === elementIndex || enablePeeking ? {} : a11yProps.nonFocusableProps;
99
99
  const handleFocus = React.useCallback(event => {
100
- if (Platform.OS === 'web' && elementIndex >= maximumItemsForSlide * (activeIndex + 1)) {
101
- goTo(activeIndex + 1);
100
+ if (Platform.OS === 'web') {
101
+ if (enablePeeking) {
102
+ if (enableDisplayMultipleItemsPerSlide) {
103
+ const startIndex = maximumItemsForSlide * activeIndex;
104
+ const endIndex = startIndex + maximumItemsForSlide - 1;
105
+ if (elementIndex < startIndex) {
106
+ if (activeIndex - 1 < 0) {
107
+ goTo(0);
108
+ } else {
109
+ goTo(activeIndex - 1);
110
+ }
111
+ } else if (elementIndex > endIndex) {
112
+ goTo(activeIndex + 1);
113
+ }
114
+ } else if (elementIndex !== activeIndex) {
115
+ if (elementIndex > activeIndex) {
116
+ goTo(activeIndex + 1);
117
+ } else if (elementIndex < activeIndex) {
118
+ goTo(activeIndex - 1);
119
+ }
120
+ }
121
+ }
102
122
  }
103
123
  if (rest.onFocus) {
104
124
  rest.onFocus(event);
105
125
  }
106
- }, [elementIndex, activeIndex, goTo, maximumItemsForSlide, rest]);
126
+ }, [elementIndex, activeIndex, goTo, maximumItemsForSlide, rest, enablePeeking, enableDisplayMultipleItemsPerSlide]);
107
127
  return /*#__PURE__*/_jsx(View, {
108
128
  style: selectContainerStyle({
109
129
  width,
@@ -36,17 +36,14 @@ const Icon = /*#__PURE__*/React.forwardRef((_ref, ref) => {
36
36
  padding: themeTokens.padding
37
37
  };
38
38
  const getIconContentForMobile = () => {
39
- if (Object.keys(paddingStyles).length) {
40
- return /*#__PURE__*/_jsx(View, {
41
- style: {
42
- backgroundColor: themeTokens.backgroundColor,
43
- borderRadius: themeTokens.borderRadius,
44
- ...paddingStyles
45
- },
46
- children: iconContent
47
- });
48
- }
49
- return iconContent;
39
+ return /*#__PURE__*/_jsx(View, {
40
+ style: {
41
+ backgroundColor: themeTokens.backgroundColor,
42
+ borderRadius: themeTokens.borderRadius,
43
+ ...paddingStyles
44
+ },
45
+ children: iconContent
46
+ });
50
47
  };
51
48
  return Platform.OS === 'web' ? /*#__PURE__*/_jsx(View, {
52
49
  ref: ref
@@ -100,7 +100,6 @@ IconText.propTypes = {
100
100
  * `<Typography>` component, or a component that renders `<Text>`.
101
101
  */
102
102
  children: PropTypes.node
103
- /* eslint-enable react/no-unused-prop-types */
104
103
  };
105
104
  const staticStyles = StyleSheet.create({
106
105
  adjustedContainer: {
@@ -1,15 +1,15 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import View from "react-native-web/dist/exports/View";
4
- import StyleSheet from "react-native-web/dist/exports/StyleSheet";
5
4
  import Text from "react-native-web/dist/exports/Text";
5
+ import StyleSheet from "react-native-web/dist/exports/StyleSheet";
6
6
  import { useThemeTokens } from '../ThemeProvider';
7
7
  import Icon from '../Icon';
8
8
  import Spacer from '../Spacer';
9
9
  import { useListboxContext } from './ListboxContext';
10
10
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
11
11
  const styles = StyleSheet.create({
12
- controlWrapper: {
12
+ container: {
13
13
  width: '100%',
14
14
  flex: 1,
15
15
  alignItems: 'center',
@@ -18,6 +18,34 @@ const styles = StyleSheet.create({
18
18
  boxSizing: 'border-box'
19
19
  }
20
20
  });
21
+ const selectTextStyles = tokens => ({
22
+ color: tokens.groupColor,
23
+ fontFamily: `${tokens.groupFontName}${tokens.groupFontWeight}normal`,
24
+ fontSize: tokens.groupFontSize,
25
+ fontWeight: tokens.groupFontWeight
26
+ });
27
+ const selectContainerStyles = tokens => ({
28
+ fontFamily: `${tokens.groupFontName}${tokens.groupFontWeight}normal`,
29
+ fontSize: tokens.groupFontSize,
30
+ color: tokens.groupColor,
31
+ textDecoration: tokens.itemTextDecoration,
32
+ backgroundColor: tokens.groupBackgroundColor,
33
+ outline: tokens.itemOutline,
34
+ minHeight: tokens.groupHeight,
35
+ borderRadius: tokens.groupBorderRadius,
36
+ paddingLeft: tokens.groupPaddingLeft - tokens.groupBorderLeftWidth,
37
+ paddingRight: tokens.groupPaddingRight - tokens.groupBorderRightWidth,
38
+ paddingTop: tokens.groupPaddingTop - tokens.groupBorderTopWidth,
39
+ paddingBottom: tokens.groupPaddingBottom - tokens.groupBorderBottomWidth,
40
+ borderLeftWidth: tokens.groupBorderLeftWidth,
41
+ borderLeftColor: tokens.groupBorderLeftColor,
42
+ borderRightWidth: tokens.groupBorderRightWidth,
43
+ borderRightColor: tokens.groupBorderRightColor,
44
+ borderTopWidth: tokens.groupBorderTopWidth,
45
+ borderTopColor: tokens.groupBorderTopColor,
46
+ borderBottomWidth: tokens.groupBorderBottomWidth,
47
+ borderBottomColor: tokens.groupBorderBottomColor
48
+ });
21
49
  const GroupControl = /*#__PURE__*/React.forwardRef((_ref, ref) => {
22
50
  let {
23
51
  expanded,
@@ -38,51 +66,17 @@ const GroupControl = /*#__PURE__*/React.forwardRef((_ref, ref) => {
38
66
  current: selectedId === id && id !== undefined,
39
67
  focus
40
68
  });
41
- const {
42
- groupFontName,
43
- groupFontWeight,
44
- groupFontSize,
45
- groupColor,
46
- groupBackgroundColor,
47
- groupBorderColor,
48
- groupBorderWidth,
49
- groupBorderRadius,
50
- groupPaddingLeft,
51
- groupPaddingRight,
52
- groupPaddingTop,
53
- groupPaddingBottom,
54
- itemTextDecoration,
55
- itemOutline,
56
- groupHeight
57
- } = tokens;
58
- const getTextStyles = () => ({
59
- color: groupColor
60
- });
61
69
  return /*#__PURE__*/_jsxs(View, {
62
70
  onPress: () => setSelectedId(id),
63
- style: [styles.controlWrapper, {
64
- fontFamily: `${groupFontName}${groupFontWeight}normal`,
65
- fontSize: groupFontSize,
66
- color: groupColor,
67
- textDecoration: itemTextDecoration,
68
- backgroundColor: groupBackgroundColor,
69
- outline: itemOutline,
70
- height: groupHeight,
71
- border: `${groupBorderWidth}px solid ${groupBorderColor}`,
72
- borderRadius: groupBorderRadius,
73
- paddingLeft: groupPaddingLeft - groupBorderWidth,
74
- paddingRight: groupPaddingRight - groupBorderWidth,
75
- paddingTop: groupPaddingTop - groupBorderWidth,
76
- paddingBottom: groupPaddingBottom - groupBorderWidth
77
- }],
71
+ style: [styles.container, selectContainerStyles(tokens)],
78
72
  ref: ref,
79
73
  children: [/*#__PURE__*/_jsx(Text, {
80
- style: getTextStyles(),
74
+ style: selectTextStyles(tokens),
81
75
  children: label
82
76
  }), /*#__PURE__*/_jsx(Spacer, {
83
77
  space: 1,
84
78
  direction: "row"
85
- }), /*#__PURE__*/_jsx(Icon, {
79
+ }), tokens.groupIcon && /*#__PURE__*/_jsx(Icon, {
86
80
  icon: tokens.groupIcon,
87
81
  tokens: {
88
82
  color: tokens.groupColor
@@ -1,10 +1,10 @@
1
1
  import React, { createElement as _createElement } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import View from "react-native-web/dist/exports/View";
4
- import StyleSheet from "react-native-web/dist/exports/StyleSheet";
5
4
  import Platform from "react-native-web/dist/exports/Platform";
5
+ import StyleSheet from "react-native-web/dist/exports/StyleSheet";
6
6
  import { useThemeTokens } from '../ThemeProvider';
7
- import { withLinkRouter, getTokensPropType } from '../utils';
7
+ import { withLinkRouter, getTokensPropType, variantProp } from '../utils';
8
8
  import ExpandCollapse from '../ExpandCollapse';
9
9
  import ListboxGroup from './ListboxGroup';
10
10
  import ListboxItem from './ListboxItem';
@@ -12,11 +12,16 @@ import { ListboxContext } from './ListboxContext';
12
12
  import DropdownOverlay from './ListboxOverlay';
13
13
  import { jsx as _jsx } from "react/jsx-runtime";
14
14
  const styles = StyleSheet.create({
15
- list: {
15
+ container: {
16
16
  padding: 0,
17
17
  margin: 0
18
18
  }
19
19
  });
20
+ const selectContainerStyles = tokens => ({
21
+ minHeight: tokens.minHeight,
22
+ minWidth: tokens.minWidth,
23
+ backgroundColor: tokens.containerBackgroundColor
24
+ });
20
25
  const getInitialOpen = (items, selectedId) => items.filter(item => item.items && item.items.some(nestedItem => (nestedItem.id ?? nestedItem.label) === selectedId)).map(item => item.id ?? item.label);
21
26
  const Listbox = /*#__PURE__*/React.forwardRef((_ref, ref) => {
22
27
  let {
@@ -30,14 +35,12 @@ const Listbox = /*#__PURE__*/React.forwardRef((_ref, ref) => {
30
35
  itemRouterProps,
31
36
  onClose,
32
37
  variant,
33
- tokens
38
+ tokens,
39
+ testID
34
40
  } = _ref;
35
41
  const initialOpen = getInitialOpen(items, defaultSelectedId);
36
42
  const [selectedId, setSelectedId] = React.useState(defaultSelectedId);
37
- const {
38
- minHeight,
39
- minWidth
40
- } = useThemeTokens('Listbox', variant, tokens);
43
+ const listboxTokens = useThemeTokens('Listbox', tokens, variant);
41
44
 
42
45
  // We need to keep track of each item's ref in order to be able to
43
46
  // focus on a specific item via keyboard navigation
@@ -96,11 +99,9 @@ const Listbox = /*#__PURE__*/React.forwardRef((_ref, ref) => {
96
99
  maxOpen: 1,
97
100
  ref: ref,
98
101
  children: expandProps => /*#__PURE__*/_jsx(View, {
99
- style: [styles.list, {
100
- minHeight,
101
- minWidth
102
- }],
103
- role: "listbox",
102
+ style: [styles.container, selectContainerStyles(listboxTokens)],
103
+ accessibilityRole: "combobox",
104
+ testID: testID,
104
105
  children: items.map((item, index) => {
105
106
  const {
106
107
  id,
@@ -162,7 +163,15 @@ Listbox.propTypes = {
162
163
  /**
163
164
  * onClose event
164
165
  */
165
- onClose: PropTypes.func
166
+ onClose: PropTypes.func,
167
+ /**
168
+ * Test ID for testing
169
+ */
170
+ testID: PropTypes.string,
171
+ /**
172
+ * Listbox variant
173
+ */
174
+ variant: variantProp.propType
166
175
  };
167
176
  Listbox.Overlay = DropdownOverlay;
168
177
  export default Listbox;
@@ -77,7 +77,8 @@ const ListboxGroup = /*#__PURE__*/React.forwardRef((_ref, ref) => {
77
77
  borderColor: 'transparent',
78
78
  borderRadius: 0,
79
79
  borderWidth: 0,
80
- marginBottom: 0
80
+ marginBottom: 0,
81
+ contentPanelBackgroundColor: 'transparent'
81
82
  },
82
83
  controlRef: ref,
83
84
  children: /*#__PURE__*/_jsx(View, {
@@ -30,12 +30,14 @@ const DropdownOverlay = /*#__PURE__*/React.forwardRef((_ref, ref) => {
30
30
  maxWidth,
31
31
  minWidth,
32
32
  onLayout,
33
- tokens
33
+ tokens,
34
+ testID
34
35
  } = _ref;
35
36
  const systemTokens = useThemeTokens('Listbox', {}, {});
36
37
  return /*#__PURE__*/_jsx(View, {
37
38
  ref: ref,
38
39
  onLayout: onLayout,
40
+ testID: testID,
39
41
  style: [overlaidPosition, {
40
42
  maxWidth,
41
43
  minWidth
@@ -74,6 +76,7 @@ DropdownOverlay.propTypes = {
74
76
  maxWidth: PropTypes.number,
75
77
  minWidth: PropTypes.number,
76
78
  onLayout: PropTypes.func,
77
- tokens: PropTypes.object
79
+ tokens: PropTypes.object,
80
+ testID: PropTypes.string
78
81
  };
79
82
  export default Platform.OS === 'web' ? withPortal(DropdownOverlay) : DropdownOverlay;
@@ -41,10 +41,14 @@ const getItemStyles = _ref => {
41
41
  color: itemColor,
42
42
  outline: itemOutline,
43
43
  textDecoration: itemTextDecoration,
44
- borderLeft: `${itemBorderLeftWidth}px solid ${itemBorderLeftColor}`,
45
- borderRight: `${itemBorderRightWidth}px solid ${itemBorderRightColor}`,
46
- borderTop: `${itemBorderTopWidth}px solid ${itemBorderTopColor}`,
47
- borderBottom: `${itemBorderBottomWidth}px solid ${itemBorderBottomColor}`,
44
+ borderLeftWidth: itemBorderLeftWidth,
45
+ borderLeftColor: itemBorderLeftColor,
46
+ borderRightWidth: itemBorderRightWidth,
47
+ borderRightColor: itemBorderRightColor,
48
+ borderTopWidth: itemBorderTopWidth,
49
+ borderTopColor: itemBorderTopColor,
50
+ borderBottomWidth: itemBorderBottomWidth,
51
+ borderBottomColor: itemBorderBottomColor,
48
52
  borderRadius: itemBorderRadius,
49
53
  justifyContent: 'center'
50
54
  };
@@ -279,6 +279,10 @@ const TextInputBase = /*#__PURE__*/React.forwardRef((_ref8, ref) => {
279
279
  // Add a space every 4 digits starting from the 5th position
280
280
  filteredText = formattedValue.replace(regex, '$1 ').trim();
281
281
  }
282
+ // Apply maxLength if provided
283
+ if (rest.maxLength && filteredText && filteredText.length > rest.maxLength) {
284
+ filteredText = filteredText.substring(0, rest.maxLength);
285
+ }
282
286
  setValue(filteredText, event);
283
287
  if (typeof onChangeText === 'function') onChangeText(filteredText, event);
284
288
  };
@@ -351,7 +355,7 @@ const TextInputBase = /*#__PURE__*/React.forwardRef((_ref8, ref) => {
351
355
  onMouseOut: handleMouseOut,
352
356
  onChange: handleChangeText,
353
357
  defaultValue: initialValue,
354
- maxLength: type === 'card' ? 19 : undefined,
358
+ maxLength: type === 'card' ? 19 : rest.maxLength,
355
359
  value: isControlled ? currentValue : undefined,
356
360
  onKeyPress
357
361
  };