@mackin.com/styleguide 10.2.2 → 10.2.4

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/index.d.ts CHANGED
@@ -863,7 +863,7 @@ interface SliderProps<T extends SliderValue> {
863
863
  innerTrackClassName?: string;
864
864
  /** Styles applied to the floating handle text with using 'showValue'. */
865
865
  sliderTextClassName?: string;
866
- /** Styles applied to the floating handle text with using 'showValue'. */
866
+ /** Sets the aria-label value. */
867
867
  ariaLabel?: (T extends number ? string : readonly string[] | undefined) | undefined;
868
868
  }
869
869
  declare const Slider: <T extends SliderValue>(p: SliderProps<T>) => React__default.JSX.Element;
package/index.esm.js CHANGED
@@ -497,6 +497,45 @@ const Button = React.forwardRef((props, ref) => {
497
497
  return content;
498
498
  });
499
499
 
500
+ /* Type is not always determinable due to the nature of custom components in typescript. For example OmniLink will not have a type of "a" but rather "OmniLink" so we have to determine
501
+ if the child component is focusable based on certain properties on the control that are found on focusable components */
502
+ const isChildFocusable = (props, type) => {
503
+ if (props.tabIndex !== undefined && props.tabIndex !== null) {
504
+ return true;
505
+ }
506
+ if (props.onClick !== undefined || props.onValueChange !== undefined) { //button or select
507
+ return true;
508
+ }
509
+ if (props.href || props.cols || props.rows || props.maxLength) {
510
+ return true;
511
+ }
512
+ if ((type === 'button' || type === 'input' || type === 'select' || type === 'textarea') && !props.disabled) {
513
+ return true;
514
+ }
515
+ return false;
516
+ };
517
+ const TabIndexContainer = (props) => {
518
+ const processElement = (node) => {
519
+ if (!React__default.isValidElement(node)) {
520
+ return node;
521
+ }
522
+ let updatedNode = node;
523
+ // Use the props-based logic to check focusability
524
+ if (isChildFocusable(node.props, node.type)) {
525
+ updatedNode = React__default.cloneElement(node, { tabIndex: props.tabIndexValue });
526
+ }
527
+ // Recursively process children
528
+ if (updatedNode.props.children) {
529
+ const clonedChildren = React__default.Children.map(updatedNode.props.children, processElement);
530
+ updatedNode = React__default.cloneElement(updatedNode, {
531
+ children: clonedChildren,
532
+ });
533
+ }
534
+ return updatedNode;
535
+ };
536
+ return (React__default.createElement(React__default.Fragment, null, React__default.Children.map(props.children, processElement)));
537
+ };
538
+
500
539
  const accordianExpandTimeMs = 250;
501
540
  const accordianMaxHeight = 1020;
502
541
  const accordianTimingFunction = 'ease-in-out';
@@ -544,7 +583,7 @@ const Accordian = (props) => {
544
583
  }
545
584
  setOpen((_a = props.open) !== null && _a !== void 0 ? _a : false);
546
585
  }, [props.open]);
547
- return (React.createElement("div", { className: "accordian" },
586
+ return (React.createElement("div", { className: "accordian", "aria-expanded": open },
548
587
  React.createElement(Button, { readOnly: props.disabled, variant: props.variant, className: cx(css({
549
588
  display: 'flex',
550
589
  alignItems: 'center',
@@ -563,7 +602,8 @@ const Accordian = (props) => {
563
602
  }, rightIcon: !props.disabled ? React.createElement(Icon, { id: open ? 'collapse' : 'expand' }) : undefined },
564
603
  React.createElement("span", null, props.header)),
565
604
  React.createElement("div", { ref: content, className: cx('accordian__body', contentStyles) },
566
- React.createElement("div", { className: expandedContentWrapperStyles }, props.children))));
605
+ React.createElement("div", { className: expandedContentWrapperStyles },
606
+ React.createElement(TabIndexContainer, { tabIndexValue: open ? 0 : -1 }, props.children)))));
567
607
  };
568
608
  const useAccordianState = (count, openIndex) => {
569
609
  const [panels, setShowPanel] = React.useState(new Array(count).fill(false).map((b, i) => {
@@ -969,13 +1009,14 @@ const defaultMaxShownValues = 7;
969
1009
  const buttonMarkerClass = 'ListItem__button';
970
1010
  const defaultOnPickFocusMs = 100;
971
1011
  const Autocomplete = (p) => {
972
- var _a;
1012
+ var _a, _b;
973
1013
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
974
1014
  const inputProps = __rest(p, ["value", "className", "inputWrapperClassName", "inputClassName", "listClassName", "listItemClassName", "listItemButtonClassName", "maxShownValues", "allowScroll", "options", "onPick", "onPickFocusWaitMs"]);
975
1015
  const theme = useThemeSafely();
976
1016
  const element = React.useRef(null);
977
1017
  const input = React.useRef(null);
978
1018
  const list = React.useRef(null);
1019
+ const [selectedResultIndex, setSelectedResultIndex] = React.useState();
979
1020
  const maxShowValues = (_a = p.maxShownValues) !== null && _a !== void 0 ? _a : defaultMaxShownValues;
980
1021
  const displayOptions = React.useMemo(() => {
981
1022
  if (!p.allowScroll) {
@@ -994,10 +1035,12 @@ const Autocomplete = (p) => {
994
1035
  if (direction === -1) {
995
1036
  buttonIndex = displayOptions.length - 1;
996
1037
  }
1038
+ setSelectedResultIndex(buttonIndex);
997
1039
  return (_a = list.current) === null || _a === void 0 ? void 0 : _a.querySelector(`.${buttonMarkerClass}${buttonIndex}`);
998
1040
  }
999
1041
  else {
1000
1042
  const nextIndex = fromIndex + direction;
1043
+ setSelectedResultIndex(nextIndex);
1001
1044
  if (nextIndex >= displayOptions.length || nextIndex < 0) {
1002
1045
  return (_b = input.current) !== null && _b !== void 0 ? _b : undefined;
1003
1046
  }
@@ -1021,6 +1064,7 @@ const Autocomplete = (p) => {
1021
1064
  if (p.round || theme.controls.borderRadius) {
1022
1065
  listBorderRadius = theme.controls.borderRadius || '0.5rem';
1023
1066
  }
1067
+ const id = (_b = p.id) !== null && _b !== void 0 ? _b : `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
1024
1068
  const onPickValue = (v) => {
1025
1069
  var _a;
1026
1070
  // the TextInput will not respond to outer value changes if it has focus.
@@ -1068,8 +1112,9 @@ const Autocomplete = (p) => {
1068
1112
  }
1069
1113
  }
1070
1114
  (_c = p.onKeyDown) === null || _c === void 0 ? void 0 : _c.call(p, e);
1071
- } })),
1072
- !!displayOptions.length && (React.createElement(List, { ref: list, className: cx(css({
1115
+ }, "aria-owns": id, "aria-expanded": !!displayOptions.length, "aria-autocomplete": "both", "aria-describedby": `${id}-aria-description` })),
1116
+ React.createElement("span", { id: `${id}-aria-description`, className: css({ display: "none" }) }, "When autocomplete results are available use up and down arrows to review and enter to select."),
1117
+ !!displayOptions.length && (React.createElement(List, { id: id, ref: list, role: "listbox", className: cx(css({
1073
1118
  position: 'absolute',
1074
1119
  width: '100%',
1075
1120
  border: theme.controls.border,
@@ -1092,7 +1137,7 @@ const Autocomplete = (p) => {
1092
1137
  }), p.listClassName) },
1093
1138
  displayOptions.map((v, listItemIndex) => {
1094
1139
  var _a;
1095
- return (React.createElement(ListItem, { key: v, variant: "full", className: p.listItemClassName },
1140
+ return (React.createElement(ListItem, { key: v, variant: "full", className: p.listItemClassName, role: "option", "aria-selected": selectedResultIndex === listItemIndex },
1096
1141
  React.createElement(Button, { title: ((_a = p.showOptionTextAsTitle) !== null && _a !== void 0 ? _a : true) ? v : undefined, onKeyDown: e => {
1097
1142
  var _a, _b;
1098
1143
  if (e.key === 'ArrowDown') {
@@ -4044,7 +4089,7 @@ const TabHeader = (p) => {
4044
4089
  buttonContent = tab.name;
4045
4090
  }
4046
4091
  return (React.createElement("li", { key: index, className: cx(tabStyles, p.tabClassName) },
4047
- React.createElement(Button, { disabled: tabsChanging, className: buttonStyles, variant: buttonVariant, title: title, readOnly: active, onClick: () => {
4092
+ React.createElement(Button, { "aria-role": "tab", "aria-selected": active, disabled: tabsChanging, className: buttonStyles, variant: buttonVariant, title: title, readOnly: active, onClick: () => {
4048
4093
  const onChange = () => {
4049
4094
  var _a;
4050
4095
  setTabIndex(index);
package/index.js CHANGED
@@ -515,6 +515,45 @@ const Button = React__namespace.forwardRef((props, ref) => {
515
515
  return content;
516
516
  });
517
517
 
518
+ /* Type is not always determinable due to the nature of custom components in typescript. For example OmniLink will not have a type of "a" but rather "OmniLink" so we have to determine
519
+ if the child component is focusable based on certain properties on the control that are found on focusable components */
520
+ const isChildFocusable = (props, type) => {
521
+ if (props.tabIndex !== undefined && props.tabIndex !== null) {
522
+ return true;
523
+ }
524
+ if (props.onClick !== undefined || props.onValueChange !== undefined) { //button or select
525
+ return true;
526
+ }
527
+ if (props.href || props.cols || props.rows || props.maxLength) {
528
+ return true;
529
+ }
530
+ if ((type === 'button' || type === 'input' || type === 'select' || type === 'textarea') && !props.disabled) {
531
+ return true;
532
+ }
533
+ return false;
534
+ };
535
+ const TabIndexContainer = (props) => {
536
+ const processElement = (node) => {
537
+ if (!React.isValidElement(node)) {
538
+ return node;
539
+ }
540
+ let updatedNode = node;
541
+ // Use the props-based logic to check focusability
542
+ if (isChildFocusable(node.props, node.type)) {
543
+ updatedNode = React.cloneElement(node, { tabIndex: props.tabIndexValue });
544
+ }
545
+ // Recursively process children
546
+ if (updatedNode.props.children) {
547
+ const clonedChildren = React.Children.map(updatedNode.props.children, processElement);
548
+ updatedNode = React.cloneElement(updatedNode, {
549
+ children: clonedChildren,
550
+ });
551
+ }
552
+ return updatedNode;
553
+ };
554
+ return (React.createElement(React.Fragment, null, React.Children.map(props.children, processElement)));
555
+ };
556
+
518
557
  const accordianExpandTimeMs = 250;
519
558
  const accordianMaxHeight = 1020;
520
559
  const accordianTimingFunction = 'ease-in-out';
@@ -562,7 +601,7 @@ const Accordian = (props) => {
562
601
  }
563
602
  setOpen((_a = props.open) !== null && _a !== void 0 ? _a : false);
564
603
  }, [props.open]);
565
- return (React__namespace.createElement("div", { className: "accordian" },
604
+ return (React__namespace.createElement("div", { className: "accordian", "aria-expanded": open },
566
605
  React__namespace.createElement(Button, { readOnly: props.disabled, variant: props.variant, className: css.cx(css.css({
567
606
  display: 'flex',
568
607
  alignItems: 'center',
@@ -581,7 +620,8 @@ const Accordian = (props) => {
581
620
  }, rightIcon: !props.disabled ? React__namespace.createElement(Icon, { id: open ? 'collapse' : 'expand' }) : undefined },
582
621
  React__namespace.createElement("span", null, props.header)),
583
622
  React__namespace.createElement("div", { ref: content, className: css.cx('accordian__body', contentStyles) },
584
- React__namespace.createElement("div", { className: expandedContentWrapperStyles }, props.children))));
623
+ React__namespace.createElement("div", { className: expandedContentWrapperStyles },
624
+ React__namespace.createElement(TabIndexContainer, { tabIndexValue: open ? 0 : -1 }, props.children)))));
585
625
  };
586
626
  const useAccordianState = (count, openIndex) => {
587
627
  const [panels, setShowPanel] = React__namespace.useState(new Array(count).fill(false).map((b, i) => {
@@ -987,13 +1027,14 @@ const defaultMaxShownValues = 7;
987
1027
  const buttonMarkerClass = 'ListItem__button';
988
1028
  const defaultOnPickFocusMs = 100;
989
1029
  const Autocomplete = (p) => {
990
- var _a;
1030
+ var _a, _b;
991
1031
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
992
1032
  const inputProps = __rest(p, ["value", "className", "inputWrapperClassName", "inputClassName", "listClassName", "listItemClassName", "listItemButtonClassName", "maxShownValues", "allowScroll", "options", "onPick", "onPickFocusWaitMs"]);
993
1033
  const theme = useThemeSafely();
994
1034
  const element = React__namespace.useRef(null);
995
1035
  const input = React__namespace.useRef(null);
996
1036
  const list = React__namespace.useRef(null);
1037
+ const [selectedResultIndex, setSelectedResultIndex] = React__namespace.useState();
997
1038
  const maxShowValues = (_a = p.maxShownValues) !== null && _a !== void 0 ? _a : defaultMaxShownValues;
998
1039
  const displayOptions = React__namespace.useMemo(() => {
999
1040
  if (!p.allowScroll) {
@@ -1012,10 +1053,12 @@ const Autocomplete = (p) => {
1012
1053
  if (direction === -1) {
1013
1054
  buttonIndex = displayOptions.length - 1;
1014
1055
  }
1056
+ setSelectedResultIndex(buttonIndex);
1015
1057
  return (_a = list.current) === null || _a === void 0 ? void 0 : _a.querySelector(`.${buttonMarkerClass}${buttonIndex}`);
1016
1058
  }
1017
1059
  else {
1018
1060
  const nextIndex = fromIndex + direction;
1061
+ setSelectedResultIndex(nextIndex);
1019
1062
  if (nextIndex >= displayOptions.length || nextIndex < 0) {
1020
1063
  return (_b = input.current) !== null && _b !== void 0 ? _b : undefined;
1021
1064
  }
@@ -1039,6 +1082,7 @@ const Autocomplete = (p) => {
1039
1082
  if (p.round || theme.controls.borderRadius) {
1040
1083
  listBorderRadius = theme.controls.borderRadius || '0.5rem';
1041
1084
  }
1085
+ const id = (_b = p.id) !== null && _b !== void 0 ? _b : `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
1042
1086
  const onPickValue = (v) => {
1043
1087
  var _a;
1044
1088
  // the TextInput will not respond to outer value changes if it has focus.
@@ -1086,8 +1130,9 @@ const Autocomplete = (p) => {
1086
1130
  }
1087
1131
  }
1088
1132
  (_c = p.onKeyDown) === null || _c === void 0 ? void 0 : _c.call(p, e);
1089
- } })),
1090
- !!displayOptions.length && (React__namespace.createElement(List, { ref: list, className: css.cx(css.css({
1133
+ }, "aria-owns": id, "aria-expanded": !!displayOptions.length, "aria-autocomplete": "both", "aria-describedby": `${id}-aria-description` })),
1134
+ React__namespace.createElement("span", { id: `${id}-aria-description`, className: css.css({ display: "none" }) }, "When autocomplete results are available use up and down arrows to review and enter to select."),
1135
+ !!displayOptions.length && (React__namespace.createElement(List, { id: id, ref: list, role: "listbox", className: css.cx(css.css({
1091
1136
  position: 'absolute',
1092
1137
  width: '100%',
1093
1138
  border: theme.controls.border,
@@ -1110,7 +1155,7 @@ const Autocomplete = (p) => {
1110
1155
  }), p.listClassName) },
1111
1156
  displayOptions.map((v, listItemIndex) => {
1112
1157
  var _a;
1113
- return (React__namespace.createElement(ListItem, { key: v, variant: "full", className: p.listItemClassName },
1158
+ return (React__namespace.createElement(ListItem, { key: v, variant: "full", className: p.listItemClassName, role: "option", "aria-selected": selectedResultIndex === listItemIndex },
1114
1159
  React__namespace.createElement(Button, { title: ((_a = p.showOptionTextAsTitle) !== null && _a !== void 0 ? _a : true) ? v : undefined, onKeyDown: e => {
1115
1160
  var _a, _b;
1116
1161
  if (e.key === 'ArrowDown') {
@@ -4062,7 +4107,7 @@ const TabHeader = (p) => {
4062
4107
  buttonContent = tab.name;
4063
4108
  }
4064
4109
  return (React__namespace.createElement("li", { key: index, className: css.cx(tabStyles, p.tabClassName) },
4065
- React__namespace.createElement(Button, { disabled: tabsChanging, className: buttonStyles, variant: buttonVariant, title: title, readOnly: active, onClick: () => {
4110
+ React__namespace.createElement(Button, { "aria-role": "tab", "aria-selected": active, disabled: tabsChanging, className: buttonStyles, variant: buttonVariant, title: title, readOnly: active, onClick: () => {
4066
4111
  const onChange = () => {
4067
4112
  var _a;
4068
4113
  setTabIndex(index);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mackin.com/styleguide",
3
- "version": "10.2.2",
3
+ "version": "10.2.4",
4
4
  "description": "",
5
5
  "main": "./index.js",
6
6
  "module": "./index.esm.js",