@qualtrics/plugin-ui-react 1.3.0 → 1.3.2-alpha.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.
@@ -1 +1 @@
1
- export function Popover(props: any, ref: any): JSX.Element;
1
+ export function Popover(props: any): JSX.Element;
package/dist/main.es.js CHANGED
@@ -1,4 +1,4 @@
1
- /*! @license @qualtrics/plugin-ui-react 1.3.0
1
+ /*! @license @qualtrics/plugin-ui-react 1.3.2-alpha.0
2
2
  Qualtrics Software License Agreement
3
3
 
4
4
  1. General.
@@ -55,17 +55,17 @@ import 'core-js/modules/es.array.find-index.js';
55
55
  import 'core-js/modules/es.array.splice.js';
56
56
  import 'core-js/modules/es.array.for-each.js';
57
57
  import 'core-js/modules/web.dom-collections.for-each.js';
58
- import React, { Component, useState, useEffect, useRef } from 'react';
58
+ import React, { Component, useState, useEffect, useRef, useCallback } from 'react';
59
59
  import _get from 'lodash.get';
60
60
  import { Button, InputGroup, Input, Icon, IconTypes, LoadingSpinner, IconButton, Tooltip, SelectMenu, MenuItem, Label } from '@qualtrics/ui-react';
61
61
  import { ErrorIcon, PipedTextIcon, RemoveItemIcon } from '@qualtrics/ui-react/icons';
62
62
  import 'core-js/modules/es.string.anchor.js';
63
- import _cloneDeep from 'lodash.clonedeep';
64
63
  import 'core-js/modules/es.string.trim.js';
65
64
  import 'core-js/modules/es.array.map.js';
66
65
  import 'core-js/modules/es.array.concat.js';
67
66
  import 'core-js/modules/es.array.filter.js';
68
67
  import 'regenerator-runtime/runtime.js';
68
+ import _cloneDeep from 'lodash.clonedeep';
69
69
  import 'core-js/modules/es.array.is-array.js';
70
70
  import { Manager, Reference, Popper } from 'react-popper';
71
71
  import ReactDOM from 'react-dom';
@@ -681,7 +681,7 @@ var DEFAULT_PLACEMENT = 'bottom-start';
681
681
  var DEFAULT_ANCHOR = React.createElement(Button, null, " anchor ");
682
682
  var defaultModifiers = [];
683
683
  initGlobalListener();
684
- function Popover(props, ref) {
684
+ function Popover(props) {
685
685
  var _useState = useState(false),
686
686
  _useState2 = _slicedToArray(_useState, 2),
687
687
  isOpen = _useState2[0],
@@ -878,7 +878,7 @@ var css_248z$8 = "div[role=\"menu\"] .piped-text-search-input-container {\n pad
878
878
  styleInject(css_248z$8);
879
879
 
880
880
  var EMBEDDED_DATA_LOCATOR = 'embeddedData';
881
- var PipedTextMenu = React.forwardRef(function (props, ref) {
881
+ var PipedTextMenu = React.forwardRef(function PipedTextMenu(props, ref) {
882
882
  var defaultMenuItems = undefined;
883
883
 
884
884
  if (typeof props.items !== 'function') {
@@ -907,8 +907,47 @@ var PipedTextMenu = React.forwardRef(function (props, ref) {
907
907
  searchValue = _useState8[0],
908
908
  setSearchValue = _useState8[1];
909
909
 
910
- var onClose = props.onClose || function () {};
910
+ var _useState9 = useState(undefined),
911
+ _useState10 = _slicedToArray(_useState9, 2),
912
+ isSubmenu = _useState10[0],
913
+ setIsSubmenu = _useState10[1];
914
+
915
+ var onClose = props.onClose || function () {
916
+ return undefined;
917
+ };
918
+
919
+ var _useState11 = useState(null),
920
+ _useState12 = _slicedToArray(_useState11, 2),
921
+ popoverEl = _useState12[0],
922
+ setPopoverEl = _useState12[1];
923
+
924
+ var popoverRefCb = useCallback(function (el) {
925
+ setPopoverEl(el);
926
+ }, []);
927
+
928
+ var _useState13 = useState(null),
929
+ _useState14 = _slicedToArray(_useState13, 2),
930
+ searchBoxEl = _useState14[0],
931
+ setSearchBoxEl = _useState14[1];
932
+
933
+ var searchBoxRefCb = useCallback(function (el) {
934
+ setSearchBoxEl(el);
935
+ }, []);
936
+ useEffect(function () {
937
+ var anchorRefEl = _get(props, 'anchorRef.current');
938
+
939
+ if (anchorRefEl) {
940
+ var anchorRefElRole = anchorRefEl.getAttribute('role');
941
+ var nextIsSubmenu = anchorRefElRole === 'menuitem';
942
+ setIsSubmenu(nextIsSubmenu);
943
+ }
911
944
 
945
+ if (isSubmenu === true) ; else if (isSubmenu === false) {
946
+ if (popoverEl !== null) {
947
+ popoverEl.focus();
948
+ }
949
+ }
950
+ }, [popoverEl, searchBoxEl, props.anchorRef, isSubmenu]);
912
951
  useEffect(function () {
913
952
  _asyncToGenerator(regeneratorRuntime.mark(function _callee() {
914
953
  var newMenuItems;
@@ -1003,6 +1042,10 @@ var PipedTextMenu = React.forwardRef(function (props, ref) {
1003
1042
  setSearchValue(searchVal);
1004
1043
  };
1005
1044
 
1045
+ var handleSearchInputClick = function handleSearchInputClick(evt) {
1046
+ evt.stopPropagation();
1047
+ };
1048
+
1006
1049
  var matchesSearch = function matchesSearch(item) {
1007
1050
  var searchVal = searchValue || '';
1008
1051
  searchVal = searchVal.trim().toLowerCase();
@@ -1032,11 +1075,12 @@ var PipedTextMenu = React.forwardRef(function (props, ref) {
1032
1075
  props.onSelected(edMenuItem);
1033
1076
  };
1034
1077
 
1035
- var getSubmenuItem = function getSubmenuItem(menuItem) {
1078
+ var SubmenuItem = function SubmenuItem(props) {
1079
+ var submenuAnchorRef = useRef(null);
1080
+ var menuItem = props.menuItem;
1036
1081
  return React.createElement("div", {
1037
1082
  "data-testid": "piped-text-submenu",
1038
- className: "piped-text-submenu-item-container",
1039
- key: menuItem.key
1083
+ className: "piped-text-submenu-item-container"
1040
1084
  }, menuItem.show && React.createElement(PipedTextMenu, {
1041
1085
  items: menuItem.submenu.items,
1042
1086
  show: menuItem.show,
@@ -1055,12 +1099,16 @@ var PipedTextMenu = React.forwardRef(function (props, ref) {
1055
1099
  ref: ref,
1056
1100
  className: "piped-text-submenu-anchor"
1057
1101
  });
1058
- }
1102
+ },
1103
+ anchorRef: submenuAnchorRef
1059
1104
  }), React.createElement("div", {
1060
1105
  className: "piped-text-submenu-item",
1106
+ "aria-haspopup": "menu",
1107
+ role: "menuitem",
1061
1108
  onMouseEnter: function onMouseEnter($e) {
1062
1109
  return submenuHovered(menuItem);
1063
- }
1110
+ },
1111
+ ref: submenuAnchorRef
1064
1112
  }, React.createElement("div", {
1065
1113
  className: "label-text"
1066
1114
  }, menuItem.label), React.createElement("div", {
@@ -1082,7 +1130,10 @@ var PipedTextMenu = React.forwardRef(function (props, ref) {
1082
1130
  var menuItemHeaderClass, menuItemDisabledClass, menuItemClassName;
1083
1131
 
1084
1132
  if (menuItem.hasSubmenu) {
1085
- return getSubmenuItem(menuItem);
1133
+ return React.createElement(SubmenuItem, {
1134
+ key: menuItem.key,
1135
+ menuItem: menuItem
1136
+ });
1086
1137
  } else if (menuItem.header === true || menuItem.disabled === true) {
1087
1138
  menuItemHeaderClass = menuItem.header === true ? 'piped-text-header' : '';
1088
1139
  menuItemDisabledClass = menuItem.disabled === true ? 'piped-text-disabled' : '';
@@ -1180,7 +1231,7 @@ var PipedTextMenu = React.forwardRef(function (props, ref) {
1180
1231
  } else {
1181
1232
  var itemsToRender = (menuItems || []).filter(matchesSearch);
1182
1233
  return React.createElement("div", {
1183
- className: "piped-text-popover-container"
1234
+ className: "piped-text-popover-menu"
1184
1235
  }, React.createElement("div", {
1185
1236
  className: "piped-text-search-input"
1186
1237
  }, React.createElement("form", {
@@ -1193,7 +1244,11 @@ var PipedTextMenu = React.forwardRef(function (props, ref) {
1193
1244
  value: searchValue,
1194
1245
  onChange: function onChange($e) {
1195
1246
  return onSearchInputChange($e);
1196
- }
1247
+ },
1248
+ onClick: function onClick($e) {
1249
+ return handleSearchInputClick($e);
1250
+ },
1251
+ ref: searchBoxRefCb
1197
1252
  }))), menuItems === undefined ? getLoadingSpinner() : getPipedTextMenuItems(itemsToRender));
1198
1253
  }
1199
1254
  };
@@ -1207,10 +1262,23 @@ var PipedTextMenu = React.forwardRef(function (props, ref) {
1207
1262
  anchor: props.anchor,
1208
1263
  ignoreClickSelector: props.ignoreClickSelector
1209
1264
  }, React.createElement("div", {
1210
- className: "piped-text-popover-container"
1265
+ className: "piped-text-popover-container",
1266
+ ref: popoverRefCb,
1267
+ onFocus: function onFocus($e) {
1268
+ return handleFocus($e);
1269
+ },
1270
+ tabIndex: props.tabIndex !== undefined ? props.tabIndex : -1
1211
1271
  }, getMenuContent()), React.createElement(OptionsPlaceholder, {
1212
1272
  optionsRef: optionsRef
1213
1273
  })));
1274
+
1275
+ function handleFocus(evt) {
1276
+ if (!isSubmenu && searchBoxEl !== null) {
1277
+ searchBoxEl.focus();
1278
+ }
1279
+
1280
+ evt.stopPropagation();
1281
+ }
1214
1282
  });
1215
1283
 
1216
1284
  function OptionsPlaceholder() {
@@ -1271,23 +1339,8 @@ function PipedTextSelect(props) {
1271
1339
  pipedTextItems = _useState4[0],
1272
1340
  setPipedTextItems = _useState4[1];
1273
1341
 
1274
- function initializePipedText() {
1275
- if (props.items) {
1276
- return pipedTextDataAdapter(props.items);
1277
- }
1278
- }
1279
-
1280
- var onClose = function onClose() {
1281
- if (props.onClose) {
1282
- props.onClose();
1283
- }
1284
- };
1285
-
1286
- var onSelect = function onSelect(selected) {
1287
- props.onSelect(selected);
1288
- onClose();
1289
- };
1290
-
1342
+ var defaultAnchorRef = useRef(null);
1343
+ var externalAnchor = props.anchor;
1291
1344
  useEffect(function () {
1292
1345
  return function () {
1293
1346
  return onClose();
@@ -1302,6 +1355,22 @@ function PipedTextSelect(props) {
1302
1355
  var formattedPipedText = initializePipedText();
1303
1356
  setPipedTextItems(formattedPipedText);
1304
1357
  }, [props.items]);
1358
+
1359
+ var onClose = function onClose() {
1360
+ if (props.onClose) {
1361
+ props.onClose();
1362
+ }
1363
+ };
1364
+
1365
+ var onSelect = function onSelect(selected) {
1366
+ props.onSelect(selected);
1367
+ onClose();
1368
+ };
1369
+
1370
+ var actualAnchor = externalAnchor !== undefined ? React.cloneElement(externalAnchor, {
1371
+ ref: externalAnchor.ref
1372
+ }) : defaultAnchor(defaultAnchorRef);
1373
+ var anchorRef = props.anchorRef || actualAnchor.ref;
1305
1374
  return React.createElement("div", {
1306
1375
  className: "piped-text-dropdown"
1307
1376
  }, isMenuOpen && React.createElement(PipedTextMenu, {
@@ -1309,9 +1378,16 @@ function PipedTextSelect(props) {
1309
1378
  show: isMenuOpen,
1310
1379
  onSelected: onSelect,
1311
1380
  items: pipedTextItems,
1312
- anchor: props.anchor || defaultAnchor,
1381
+ anchor: actualAnchor,
1382
+ anchorRef: anchorRef,
1313
1383
  ignoreClickSelector: props.ignoreClickSelector
1314
1384
  }));
1385
+
1386
+ function initializePipedText() {
1387
+ if (props.items) {
1388
+ return pipedTextDataAdapter(props.items);
1389
+ }
1390
+ }
1315
1391
  }
1316
1392
 
1317
1393
  var css_248z$6 = ".piped-text-input .piped-text-input-toggle, .piped-text-input .piped-text-select-container {\n display: inline-block; }\n\n.piped-text-input .piped-text-input-toggle {\n display: flex; }\n .piped-text-input .piped-text-input-toggle input {\n flex: 1 1;\n box-sizing: border-box; }\n .piped-text-input .piped-text-input-toggle input:first-child {\n border-right: 0;\n border-top-right-radius: 0;\n border-bottom-right-radius: 0; }\n .piped-text-input .piped-text-input-toggle.multiline-input textarea {\n flex: 1 1;\n box-sizing: border-box; }\n .piped-text-input .piped-text-input-toggle input + button, .piped-text-input .piped-text-input-toggle textarea + button {\n margin-left: 4px; }\n\n.piped-text-input .addon-text-container {\n /*\n These styles are intended to match CFE QDS components,\n notably FormField and InputField.\n See: https://gitlab-app.eng.qops.net/cfe/ui/-/blob/master/packages/ui-react/src/FormField/Caption.less\n */\n margin-top: 6px; }\n .piped-text-input .addon-text-container .helper-text, .piped-text-input .addon-text-container .error-text {\n font-size: 12px; }\n .piped-text-input .addon-text-container .helper-text {\n color: #74777a;\n fill: #74777a; }\n .piped-text-input .addon-text-container .helper-text + .error-text {\n margin-top: 4px; }\n .piped-text-input .addon-text-container .error-text {\n display: flex;\n align-items: center;\n color: #b00;\n fill: #b00; }\n .piped-text-input .addon-text-container .error-text .error-text-icon svg {\n margin-left: -2px;\n margin-right: 3px;\n width: 16px;\n height: 16px; }\n";
@@ -1320,6 +1396,7 @@ styleInject(css_248z$6);
1320
1396
  function PipedTextInput(props) {
1321
1397
  var _a = useState(false), showMenu = _a[0], setShowMenu = _a[1];
1322
1398
  var _b = useState(), cursorPosition = _b[0], setCursorPosition = _b[1];
1399
+ var pipedTextButtonRef = useRef(null);
1323
1400
  /*
1324
1401
  This has to be an Object type ref instead of 'string' or React will lose
1325
1402
  track of state, and the supplied props input value will become stale
@@ -1354,10 +1431,10 @@ function PipedTextInput(props) {
1354
1431
  React.createElement(Input, __assign({ value: inputRefValue.value, onChange: function (evt) {
1355
1432
  onInputChange(evt);
1356
1433
  }, onBlur: onInputBlur, placeholder: placeholder, multiline: multiline }, restProps)),
1357
- React.createElement(IconButton, { "aria-label": "PipedText", size: "medium", onClick: toggleMenu },
1434
+ React.createElement(IconButton, { "aria-label": "PipedText", "aria-haspopup": "menu", "aria-expanded": showMenu ? true : undefined, role: "button", size: "medium", onClick: toggleMenu, ref: pipedTextButtonRef },
1358
1435
  React.createElement(PipedTextIcon, null)),
1359
1436
  React.createElement("div", { className: "piped-text-select-container" },
1360
- React.createElement(PipedTextSelect, { items: items, onSelect: pipedTextSelected, show: showMenu, onClose: handleClose, ignoreClickSelector: "piped-text-input-toggle" }))),
1437
+ React.createElement(PipedTextSelect, { items: items, onSelect: pipedTextSelected, show: showMenu, onClose: handleClose, ignoreClickSelector: "piped-text-input-toggle", anchorRef: pipedTextButtonRef }))),
1361
1438
  showAddonText() && (React.createElement("div", { className: "addon-text-container" },
1362
1439
  helperText && (React.createElement("div", { className: "helper-text" }, helperText)),
1363
1440
  errorText && (React.createElement("div", { className: "error-text" },
@@ -1512,13 +1589,28 @@ function FieldRow(props) {
1512
1589
  }
1513
1590
 
1514
1591
  if (props.required || !props.children || Array.isArray(children) && children.length === 0) {
1515
- return React.createElement("div", {
1516
- className: "required-indicator-container"
1517
- }, React.createElement("div", {
1518
- className: "required-indicator-text"
1519
- }, props.fieldName), props.required && React.createElement("div", {
1520
- className: "required-indicator-symbol"
1521
- }, "*"));
1592
+ if (props.subMenu) {
1593
+ return React.createElement(SelectMenu, {
1594
+ searchable: props.searchable !== undefined && props.searchable !== false,
1595
+ defaultLabel: props.fieldName || '',
1596
+ label: props.fieldName,
1597
+ className: "full-width left-align",
1598
+ onChange: handleFieldSwitch
1599
+ }, createMenuItems([{
1600
+ id: props.id,
1601
+ name: props.fieldName,
1602
+ subMenu: props.subMenu,
1603
+ subMenuLabel: props.subMenuLabel
1604
+ }]));
1605
+ } else {
1606
+ return React.createElement("div", {
1607
+ className: "required-indicator-container"
1608
+ }, React.createElement("div", {
1609
+ className: "required-indicator-text"
1610
+ }, props.fieldName), props.required && React.createElement("div", {
1611
+ className: "required-indicator-symbol"
1612
+ }, "*"));
1613
+ }
1522
1614
  }
1523
1615
 
1524
1616
  return React.createElement(SelectMenu, {
@@ -1527,12 +1619,7 @@ function FieldRow(props) {
1527
1619
  label: props.fieldName,
1528
1620
  className: "full-width left-align",
1529
1621
  onChange: handleFieldSwitch
1530
- }, children.map(function (field) {
1531
- return React.createElement(MenuItem, {
1532
- value: field.id,
1533
- key: field.id
1534
- }, field.name);
1535
- }));
1622
+ }, createMenuItems(children));
1536
1623
  };
1537
1624
 
1538
1625
  var tooltipContent = React.createElement("div", null, props.nameTooltip);
@@ -1543,6 +1630,22 @@ function FieldRow(props) {
1543
1630
  }, React.createElement("div", null, getInternalContent()));
1544
1631
  };
1545
1632
 
1633
+ var createMenuItems = function createMenuItems(items) {
1634
+ return items.map(function (_ref) {
1635
+ var id = _ref.id,
1636
+ name = _ref.name,
1637
+ subMenu = _ref.subMenu,
1638
+ subMenuLabel = _ref.subMenuLabel;
1639
+ return subMenu !== undefined ? React.createElement(SelectMenu, {
1640
+ label: subMenuLabel ? subMenuLabel : name,
1641
+ key: id
1642
+ }, createMenuItems(subMenu)) : React.createElement(MenuItem, {
1643
+ key: id,
1644
+ value: id
1645
+ }, name);
1646
+ });
1647
+ };
1648
+
1546
1649
  var getFieldInput = function getFieldInput() {
1547
1650
  var getInternalContent = function getInternalContent() {
1548
1651
  if (props.customInput) {
@@ -1568,7 +1671,7 @@ function FieldRow(props) {
1568
1671
  return React.createElement("div", {
1569
1672
  className: "field-row"
1570
1673
  }, React.createElement("div", {
1571
- className: "field-selector ".concat(props.required ? 'required-field' : '')
1674
+ className: "field-selector ".concat(props.required && !props.subMenu ? 'required-field' : '')
1572
1675
  }, getFieldSelector()), React.createElement("div", {
1573
1676
  className: "field-input-container"
1574
1677
  }, React.createElement("div", {
@@ -1644,6 +1747,8 @@ function TaskFields(props) {
1644
1747
  required: field.required,
1645
1748
  fieldName: field.name,
1646
1749
  id: field.id,
1750
+ subMenu: field.subMenu,
1751
+ subMenuLabel: field.subMenuLabel,
1647
1752
  nameTooltip: field.type === CUSTOM_TYPE ? _get(props, 'customFieldOptions.nameTooltip', '') : field.nameTooltip,
1648
1753
  valueTooltip: field.type === CUSTOM_TYPE ? _get(props, 'customFieldOptions.valueTooltip', '') : field.valueTooltip,
1649
1754
  onFieldNameChange: onFieldNameChange,
@@ -1657,10 +1762,26 @@ function TaskFields(props) {
1657
1762
  customNamePlaceholder: _get(props, 'customFieldOptions.namePlaceholder', ''),
1658
1763
  pipedTextItems: props.pipedTextItems,
1659
1764
  searchable: props.searchable
1660
- }, allFieldOptions && availableFields);
1765
+ }, allFieldOptions && getFieldsForFieldRow(availableFields, allFieldOptions, field.id));
1661
1766
  });
1662
1767
  };
1663
1768
 
1769
+ var getFieldsForFieldRow = function getFieldsForFieldRow(allAvailableFields, allFields, fieldId) {
1770
+ var fieldsToAdd = allAvailableFields.slice();
1771
+ allFields.forEach(function (field) {
1772
+ if (field.subMenu !== undefined && fieldId === field.id) {
1773
+ var foundIndex = fieldsToAdd.findIndex(function (fieldToAdd) {
1774
+ return fieldToAdd.id === field.id;
1775
+ });
1776
+
1777
+ if (foundIndex === -1) {
1778
+ fieldsToAdd.push(field);
1779
+ }
1780
+ }
1781
+ });
1782
+ return fieldsToAdd;
1783
+ };
1784
+
1664
1785
  var onFieldAdd = function onFieldAdd(fieldId) {
1665
1786
  if (typeof props.onFieldAdd === 'function') {
1666
1787
  props.onFieldAdd(fieldId);
@@ -1680,7 +1801,9 @@ function TaskFields(props) {
1680
1801
  type: field.type,
1681
1802
  required: field.required,
1682
1803
  key: field.id,
1683
- id: field.id
1804
+ id: field.id,
1805
+ subMenu: field.subMenu,
1806
+ subMenuLabel: field.subMenuLabel
1684
1807
  });
1685
1808
  });
1686
1809
  };
@@ -1733,7 +1856,9 @@ function TaskFields(props) {
1733
1856
  children: foundNextChild.props.children,
1734
1857
  nameTooltip: foundNextChild.props.nameTooltip,
1735
1858
  valueTooltip: foundNextChild.props.valueTooltip,
1736
- valuePlaceholder: foundNextChild.props.valuePlaceholder
1859
+ valuePlaceholder: foundNextChild.props.valuePlaceholder,
1860
+ subMenu: foundNextChild.props.subMenu,
1861
+ subMenuLabel: foundNextChild.props.subMenuLabel
1737
1862
  });
1738
1863
  }
1739
1864
 
@@ -1754,7 +1879,9 @@ function TaskFields(props) {
1754
1879
  nameTooltip: field.props.nameTooltip,
1755
1880
  valueTooltip: field.props.valueTooltip,
1756
1881
  value: field.props.value || undefined,
1757
- valuePlaceholder: field.props.valuePlaceholder
1882
+ valuePlaceholder: field.props.valuePlaceholder,
1883
+ subMenu: field.props.subMenu,
1884
+ subMenuLabel: field.props.subMenuLabel
1758
1885
  });
1759
1886
  }
1760
1887
  });
@@ -1780,7 +1907,9 @@ function TaskFields(props) {
1780
1907
  nameTooltip: _get(props, 'customFieldOptions.nameTooltip', ''),
1781
1908
  valueTooltip: _get(props, 'customFieldOptions.valueTooltip', ''),
1782
1909
  namePlaceholder: _get(props, 'customFieldOptions.namePlaceholder', ''),
1783
- type: CUSTOM_TYPE
1910
+ type: CUSTOM_TYPE,
1911
+ subMenu: currentField.subMenu,
1912
+ subMenuLabel: currentField.subMenuLabel
1784
1913
  };
1785
1914
  nextFieldsInUse.push(customFieldValue);
1786
1915
  }
@@ -1818,7 +1947,9 @@ function TaskFields(props) {
1818
1947
  children: childProps.children,
1819
1948
  nameTooltip: childProps.nameTooltip,
1820
1949
  valueTooltip: childProps.valueTooltip,
1821
- valuePlaceholder: childProps.valuePlaceholder
1950
+ valuePlaceholder: childProps.valuePlaceholder,
1951
+ subMenu: childProps.subMenu,
1952
+ subMenuLabel: childProps.subMenuLabel
1822
1953
  };
1823
1954
  });
1824
1955
  var nextFieldsInUse = getFieldsInUse(nextAllFieldOptions);
@@ -1853,6 +1984,7 @@ function TaskFields(props) {
1853
1984
  }, React.createElement(AddFieldButton, {
1854
1985
  label: props.addFieldLabel,
1855
1986
  onFieldAdded: onFieldAdd,
1987
+ disableAddFieldPopover: props.disableAddFieldPopover,
1856
1988
  allowCustom: props.customFieldOptions,
1857
1989
  customLabel: _get(props, 'customFieldOptions.label', ''),
1858
1990
  onCustomFieldAdded: props.customFieldOptions ? onCustomFieldAdd : function () {}
@@ -1862,6 +1994,7 @@ function TaskFields(props) {
1862
1994
  function AddFieldButton(props) {
1863
1995
  var label = props.label || '';
1864
1996
  var customLabel = props.customLabel || '';
1997
+ var disableAddFieldPopover = props.disableAddFieldPopover;
1865
1998
 
1866
1999
  var _useState7 = useState(false),
1867
2000
  _useState8 = _slicedToArray(_useState7, 2),
@@ -1883,15 +2016,30 @@ function AddFieldButton(props) {
1883
2016
  setPopoverShow(false);
1884
2017
  };
1885
2018
 
2019
+ var children = [];
2020
+
1886
2021
  var onPopoverAnchorClick = function onPopoverAnchorClick() {
1887
- setPopoverShow(!popoverShow);
2022
+ if (!disableAddFieldPopover) {
2023
+ setPopoverShow(!popoverShow);
2024
+ } else {
2025
+ props.onFieldAdded(props.children !== 0 ? getFieldIdWhenPopoverIsClicked(props.children[0].props) : '');
2026
+ }
2027
+ };
2028
+
2029
+ var getFieldIdWhenPopoverIsClicked = function getFieldIdWhenPopoverIsClicked(field) {
2030
+ if (field.subMenu) {
2031
+ return getFieldIdWhenPopoverIsClicked(field.subMenu[0]);
2032
+ } else {
2033
+ return field.id;
2034
+ }
1888
2035
  };
1889
2036
 
1890
- var children = props.children.map(function (child) {
2037
+ children = props.children.map(function (child) {
1891
2038
  return React.createElement(FieldItem, {
1892
2039
  name: child.props.name,
1893
2040
  key: child.props.id,
1894
2041
  id: child.props.id,
2042
+ subMenu: child.props.subMenu,
1895
2043
  onClick: function onClick(fieldId) {
1896
2044
  setPopoverShow(false);
1897
2045
  props.onFieldAdded(fieldId);
package/dist/main.js CHANGED
@@ -1,4 +1,4 @@
1
- /*! @license @qualtrics/plugin-ui-react 1.3.0
1
+ /*! @license @qualtrics/plugin-ui-react 1.3.2-alpha.0
2
2
  Qualtrics Software License Agreement
3
3
 
4
4
  1. General.
@@ -64,12 +64,12 @@ var _get = require('lodash.get');
64
64
  var uiReact = require('@qualtrics/ui-react');
65
65
  var icons = require('@qualtrics/ui-react/icons');
66
66
  require('core-js/modules/es.string.anchor.js');
67
- var _cloneDeep = require('lodash.clonedeep');
68
67
  require('core-js/modules/es.string.trim.js');
69
68
  require('core-js/modules/es.array.map.js');
70
69
  require('core-js/modules/es.array.concat.js');
71
70
  require('core-js/modules/es.array.filter.js');
72
71
  require('regenerator-runtime/runtime.js');
72
+ var _cloneDeep = require('lodash.clonedeep');
73
73
  require('core-js/modules/es.array.is-array.js');
74
74
  var reactPopper = require('react-popper');
75
75
  var ReactDOM = require('react-dom');
@@ -694,7 +694,7 @@ var DEFAULT_PLACEMENT = 'bottom-start';
694
694
  var DEFAULT_ANCHOR = React__default["default"].createElement(uiReact.Button, null, " anchor ");
695
695
  var defaultModifiers = [];
696
696
  initGlobalListener();
697
- function Popover(props, ref) {
697
+ function Popover(props) {
698
698
  var _useState = React.useState(false),
699
699
  _useState2 = _slicedToArray(_useState, 2),
700
700
  isOpen = _useState2[0],
@@ -891,7 +891,7 @@ var css_248z$8 = "div[role=\"menu\"] .piped-text-search-input-container {\n pad
891
891
  styleInject(css_248z$8);
892
892
 
893
893
  var EMBEDDED_DATA_LOCATOR = 'embeddedData';
894
- var PipedTextMenu = React__default["default"].forwardRef(function (props, ref) {
894
+ var PipedTextMenu = React__default["default"].forwardRef(function PipedTextMenu(props, ref) {
895
895
  var defaultMenuItems = undefined;
896
896
 
897
897
  if (typeof props.items !== 'function') {
@@ -920,8 +920,47 @@ var PipedTextMenu = React__default["default"].forwardRef(function (props, ref) {
920
920
  searchValue = _useState8[0],
921
921
  setSearchValue = _useState8[1];
922
922
 
923
- var onClose = props.onClose || function () {};
923
+ var _useState9 = React.useState(undefined),
924
+ _useState10 = _slicedToArray(_useState9, 2),
925
+ isSubmenu = _useState10[0],
926
+ setIsSubmenu = _useState10[1];
927
+
928
+ var onClose = props.onClose || function () {
929
+ return undefined;
930
+ };
931
+
932
+ var _useState11 = React.useState(null),
933
+ _useState12 = _slicedToArray(_useState11, 2),
934
+ popoverEl = _useState12[0],
935
+ setPopoverEl = _useState12[1];
936
+
937
+ var popoverRefCb = React.useCallback(function (el) {
938
+ setPopoverEl(el);
939
+ }, []);
940
+
941
+ var _useState13 = React.useState(null),
942
+ _useState14 = _slicedToArray(_useState13, 2),
943
+ searchBoxEl = _useState14[0],
944
+ setSearchBoxEl = _useState14[1];
945
+
946
+ var searchBoxRefCb = React.useCallback(function (el) {
947
+ setSearchBoxEl(el);
948
+ }, []);
949
+ React.useEffect(function () {
950
+ var anchorRefEl = _get__default["default"](props, 'anchorRef.current');
951
+
952
+ if (anchorRefEl) {
953
+ var anchorRefElRole = anchorRefEl.getAttribute('role');
954
+ var nextIsSubmenu = anchorRefElRole === 'menuitem';
955
+ setIsSubmenu(nextIsSubmenu);
956
+ }
924
957
 
958
+ if (isSubmenu === true) ; else if (isSubmenu === false) {
959
+ if (popoverEl !== null) {
960
+ popoverEl.focus();
961
+ }
962
+ }
963
+ }, [popoverEl, searchBoxEl, props.anchorRef, isSubmenu]);
925
964
  React.useEffect(function () {
926
965
  _asyncToGenerator(regeneratorRuntime.mark(function _callee() {
927
966
  var newMenuItems;
@@ -1016,6 +1055,10 @@ var PipedTextMenu = React__default["default"].forwardRef(function (props, ref) {
1016
1055
  setSearchValue(searchVal);
1017
1056
  };
1018
1057
 
1058
+ var handleSearchInputClick = function handleSearchInputClick(evt) {
1059
+ evt.stopPropagation();
1060
+ };
1061
+
1019
1062
  var matchesSearch = function matchesSearch(item) {
1020
1063
  var searchVal = searchValue || '';
1021
1064
  searchVal = searchVal.trim().toLowerCase();
@@ -1045,11 +1088,12 @@ var PipedTextMenu = React__default["default"].forwardRef(function (props, ref) {
1045
1088
  props.onSelected(edMenuItem);
1046
1089
  };
1047
1090
 
1048
- var getSubmenuItem = function getSubmenuItem(menuItem) {
1091
+ var SubmenuItem = function SubmenuItem(props) {
1092
+ var submenuAnchorRef = React.useRef(null);
1093
+ var menuItem = props.menuItem;
1049
1094
  return React__default["default"].createElement("div", {
1050
1095
  "data-testid": "piped-text-submenu",
1051
- className: "piped-text-submenu-item-container",
1052
- key: menuItem.key
1096
+ className: "piped-text-submenu-item-container"
1053
1097
  }, menuItem.show && React__default["default"].createElement(PipedTextMenu, {
1054
1098
  items: menuItem.submenu.items,
1055
1099
  show: menuItem.show,
@@ -1068,12 +1112,16 @@ var PipedTextMenu = React__default["default"].forwardRef(function (props, ref) {
1068
1112
  ref: ref,
1069
1113
  className: "piped-text-submenu-anchor"
1070
1114
  });
1071
- }
1115
+ },
1116
+ anchorRef: submenuAnchorRef
1072
1117
  }), React__default["default"].createElement("div", {
1073
1118
  className: "piped-text-submenu-item",
1119
+ "aria-haspopup": "menu",
1120
+ role: "menuitem",
1074
1121
  onMouseEnter: function onMouseEnter($e) {
1075
1122
  return submenuHovered(menuItem);
1076
- }
1123
+ },
1124
+ ref: submenuAnchorRef
1077
1125
  }, React__default["default"].createElement("div", {
1078
1126
  className: "label-text"
1079
1127
  }, menuItem.label), React__default["default"].createElement("div", {
@@ -1095,7 +1143,10 @@ var PipedTextMenu = React__default["default"].forwardRef(function (props, ref) {
1095
1143
  var menuItemHeaderClass, menuItemDisabledClass, menuItemClassName;
1096
1144
 
1097
1145
  if (menuItem.hasSubmenu) {
1098
- return getSubmenuItem(menuItem);
1146
+ return React__default["default"].createElement(SubmenuItem, {
1147
+ key: menuItem.key,
1148
+ menuItem: menuItem
1149
+ });
1099
1150
  } else if (menuItem.header === true || menuItem.disabled === true) {
1100
1151
  menuItemHeaderClass = menuItem.header === true ? 'piped-text-header' : '';
1101
1152
  menuItemDisabledClass = menuItem.disabled === true ? 'piped-text-disabled' : '';
@@ -1193,7 +1244,7 @@ var PipedTextMenu = React__default["default"].forwardRef(function (props, ref) {
1193
1244
  } else {
1194
1245
  var itemsToRender = (menuItems || []).filter(matchesSearch);
1195
1246
  return React__default["default"].createElement("div", {
1196
- className: "piped-text-popover-container"
1247
+ className: "piped-text-popover-menu"
1197
1248
  }, React__default["default"].createElement("div", {
1198
1249
  className: "piped-text-search-input"
1199
1250
  }, React__default["default"].createElement("form", {
@@ -1206,7 +1257,11 @@ var PipedTextMenu = React__default["default"].forwardRef(function (props, ref) {
1206
1257
  value: searchValue,
1207
1258
  onChange: function onChange($e) {
1208
1259
  return onSearchInputChange($e);
1209
- }
1260
+ },
1261
+ onClick: function onClick($e) {
1262
+ return handleSearchInputClick($e);
1263
+ },
1264
+ ref: searchBoxRefCb
1210
1265
  }))), menuItems === undefined ? getLoadingSpinner() : getPipedTextMenuItems(itemsToRender));
1211
1266
  }
1212
1267
  };
@@ -1220,10 +1275,23 @@ var PipedTextMenu = React__default["default"].forwardRef(function (props, ref) {
1220
1275
  anchor: props.anchor,
1221
1276
  ignoreClickSelector: props.ignoreClickSelector
1222
1277
  }, React__default["default"].createElement("div", {
1223
- className: "piped-text-popover-container"
1278
+ className: "piped-text-popover-container",
1279
+ ref: popoverRefCb,
1280
+ onFocus: function onFocus($e) {
1281
+ return handleFocus($e);
1282
+ },
1283
+ tabIndex: props.tabIndex !== undefined ? props.tabIndex : -1
1224
1284
  }, getMenuContent()), React__default["default"].createElement(OptionsPlaceholder, {
1225
1285
  optionsRef: optionsRef
1226
1286
  })));
1287
+
1288
+ function handleFocus(evt) {
1289
+ if (!isSubmenu && searchBoxEl !== null) {
1290
+ searchBoxEl.focus();
1291
+ }
1292
+
1293
+ evt.stopPropagation();
1294
+ }
1227
1295
  });
1228
1296
 
1229
1297
  function OptionsPlaceholder() {
@@ -1284,23 +1352,8 @@ function PipedTextSelect(props) {
1284
1352
  pipedTextItems = _useState4[0],
1285
1353
  setPipedTextItems = _useState4[1];
1286
1354
 
1287
- function initializePipedText() {
1288
- if (props.items) {
1289
- return pipedTextDataAdapter(props.items);
1290
- }
1291
- }
1292
-
1293
- var onClose = function onClose() {
1294
- if (props.onClose) {
1295
- props.onClose();
1296
- }
1297
- };
1298
-
1299
- var onSelect = function onSelect(selected) {
1300
- props.onSelect(selected);
1301
- onClose();
1302
- };
1303
-
1355
+ var defaultAnchorRef = React.useRef(null);
1356
+ var externalAnchor = props.anchor;
1304
1357
  React.useEffect(function () {
1305
1358
  return function () {
1306
1359
  return onClose();
@@ -1315,6 +1368,22 @@ function PipedTextSelect(props) {
1315
1368
  var formattedPipedText = initializePipedText();
1316
1369
  setPipedTextItems(formattedPipedText);
1317
1370
  }, [props.items]);
1371
+
1372
+ var onClose = function onClose() {
1373
+ if (props.onClose) {
1374
+ props.onClose();
1375
+ }
1376
+ };
1377
+
1378
+ var onSelect = function onSelect(selected) {
1379
+ props.onSelect(selected);
1380
+ onClose();
1381
+ };
1382
+
1383
+ var actualAnchor = externalAnchor !== undefined ? React__default["default"].cloneElement(externalAnchor, {
1384
+ ref: externalAnchor.ref
1385
+ }) : defaultAnchor(defaultAnchorRef);
1386
+ var anchorRef = props.anchorRef || actualAnchor.ref;
1318
1387
  return React__default["default"].createElement("div", {
1319
1388
  className: "piped-text-dropdown"
1320
1389
  }, isMenuOpen && React__default["default"].createElement(PipedTextMenu, {
@@ -1322,9 +1391,16 @@ function PipedTextSelect(props) {
1322
1391
  show: isMenuOpen,
1323
1392
  onSelected: onSelect,
1324
1393
  items: pipedTextItems,
1325
- anchor: props.anchor || defaultAnchor,
1394
+ anchor: actualAnchor,
1395
+ anchorRef: anchorRef,
1326
1396
  ignoreClickSelector: props.ignoreClickSelector
1327
1397
  }));
1398
+
1399
+ function initializePipedText() {
1400
+ if (props.items) {
1401
+ return pipedTextDataAdapter(props.items);
1402
+ }
1403
+ }
1328
1404
  }
1329
1405
 
1330
1406
  var css_248z$6 = ".piped-text-input .piped-text-input-toggle, .piped-text-input .piped-text-select-container {\n display: inline-block; }\n\n.piped-text-input .piped-text-input-toggle {\n display: flex; }\n .piped-text-input .piped-text-input-toggle input {\n flex: 1 1;\n box-sizing: border-box; }\n .piped-text-input .piped-text-input-toggle input:first-child {\n border-right: 0;\n border-top-right-radius: 0;\n border-bottom-right-radius: 0; }\n .piped-text-input .piped-text-input-toggle.multiline-input textarea {\n flex: 1 1;\n box-sizing: border-box; }\n .piped-text-input .piped-text-input-toggle input + button, .piped-text-input .piped-text-input-toggle textarea + button {\n margin-left: 4px; }\n\n.piped-text-input .addon-text-container {\n /*\n These styles are intended to match CFE QDS components,\n notably FormField and InputField.\n See: https://gitlab-app.eng.qops.net/cfe/ui/-/blob/master/packages/ui-react/src/FormField/Caption.less\n */\n margin-top: 6px; }\n .piped-text-input .addon-text-container .helper-text, .piped-text-input .addon-text-container .error-text {\n font-size: 12px; }\n .piped-text-input .addon-text-container .helper-text {\n color: #74777a;\n fill: #74777a; }\n .piped-text-input .addon-text-container .helper-text + .error-text {\n margin-top: 4px; }\n .piped-text-input .addon-text-container .error-text {\n display: flex;\n align-items: center;\n color: #b00;\n fill: #b00; }\n .piped-text-input .addon-text-container .error-text .error-text-icon svg {\n margin-left: -2px;\n margin-right: 3px;\n width: 16px;\n height: 16px; }\n";
@@ -1333,6 +1409,7 @@ styleInject(css_248z$6);
1333
1409
  function PipedTextInput(props) {
1334
1410
  var _a = React.useState(false), showMenu = _a[0], setShowMenu = _a[1];
1335
1411
  var _b = React.useState(), cursorPosition = _b[0], setCursorPosition = _b[1];
1412
+ var pipedTextButtonRef = React.useRef(null);
1336
1413
  /*
1337
1414
  This has to be an Object type ref instead of 'string' or React will lose
1338
1415
  track of state, and the supplied props input value will become stale
@@ -1367,10 +1444,10 @@ function PipedTextInput(props) {
1367
1444
  React__default["default"].createElement(uiReact.Input, __assign({ value: inputRefValue.value, onChange: function (evt) {
1368
1445
  onInputChange(evt);
1369
1446
  }, onBlur: onInputBlur, placeholder: placeholder, multiline: multiline }, restProps)),
1370
- React__default["default"].createElement(uiReact.IconButton, { "aria-label": "PipedText", size: "medium", onClick: toggleMenu },
1447
+ React__default["default"].createElement(uiReact.IconButton, { "aria-label": "PipedText", "aria-haspopup": "menu", "aria-expanded": showMenu ? true : undefined, role: "button", size: "medium", onClick: toggleMenu, ref: pipedTextButtonRef },
1371
1448
  React__default["default"].createElement(icons.PipedTextIcon, null)),
1372
1449
  React__default["default"].createElement("div", { className: "piped-text-select-container" },
1373
- React__default["default"].createElement(PipedTextSelect, { items: items, onSelect: pipedTextSelected, show: showMenu, onClose: handleClose, ignoreClickSelector: "piped-text-input-toggle" }))),
1450
+ React__default["default"].createElement(PipedTextSelect, { items: items, onSelect: pipedTextSelected, show: showMenu, onClose: handleClose, ignoreClickSelector: "piped-text-input-toggle", anchorRef: pipedTextButtonRef }))),
1374
1451
  showAddonText() && (React__default["default"].createElement("div", { className: "addon-text-container" },
1375
1452
  helperText && (React__default["default"].createElement("div", { className: "helper-text" }, helperText)),
1376
1453
  errorText && (React__default["default"].createElement("div", { className: "error-text" },
@@ -1525,13 +1602,28 @@ function FieldRow(props) {
1525
1602
  }
1526
1603
 
1527
1604
  if (props.required || !props.children || Array.isArray(children) && children.length === 0) {
1528
- return React__default["default"].createElement("div", {
1529
- className: "required-indicator-container"
1530
- }, React__default["default"].createElement("div", {
1531
- className: "required-indicator-text"
1532
- }, props.fieldName), props.required && React__default["default"].createElement("div", {
1533
- className: "required-indicator-symbol"
1534
- }, "*"));
1605
+ if (props.subMenu) {
1606
+ return React__default["default"].createElement(uiReact.SelectMenu, {
1607
+ searchable: props.searchable !== undefined && props.searchable !== false,
1608
+ defaultLabel: props.fieldName || '',
1609
+ label: props.fieldName,
1610
+ className: "full-width left-align",
1611
+ onChange: handleFieldSwitch
1612
+ }, createMenuItems([{
1613
+ id: props.id,
1614
+ name: props.fieldName,
1615
+ subMenu: props.subMenu,
1616
+ subMenuLabel: props.subMenuLabel
1617
+ }]));
1618
+ } else {
1619
+ return React__default["default"].createElement("div", {
1620
+ className: "required-indicator-container"
1621
+ }, React__default["default"].createElement("div", {
1622
+ className: "required-indicator-text"
1623
+ }, props.fieldName), props.required && React__default["default"].createElement("div", {
1624
+ className: "required-indicator-symbol"
1625
+ }, "*"));
1626
+ }
1535
1627
  }
1536
1628
 
1537
1629
  return React__default["default"].createElement(uiReact.SelectMenu, {
@@ -1540,12 +1632,7 @@ function FieldRow(props) {
1540
1632
  label: props.fieldName,
1541
1633
  className: "full-width left-align",
1542
1634
  onChange: handleFieldSwitch
1543
- }, children.map(function (field) {
1544
- return React__default["default"].createElement(uiReact.MenuItem, {
1545
- value: field.id,
1546
- key: field.id
1547
- }, field.name);
1548
- }));
1635
+ }, createMenuItems(children));
1549
1636
  };
1550
1637
 
1551
1638
  var tooltipContent = React__default["default"].createElement("div", null, props.nameTooltip);
@@ -1556,6 +1643,22 @@ function FieldRow(props) {
1556
1643
  }, React__default["default"].createElement("div", null, getInternalContent()));
1557
1644
  };
1558
1645
 
1646
+ var createMenuItems = function createMenuItems(items) {
1647
+ return items.map(function (_ref) {
1648
+ var id = _ref.id,
1649
+ name = _ref.name,
1650
+ subMenu = _ref.subMenu,
1651
+ subMenuLabel = _ref.subMenuLabel;
1652
+ return subMenu !== undefined ? React__default["default"].createElement(uiReact.SelectMenu, {
1653
+ label: subMenuLabel ? subMenuLabel : name,
1654
+ key: id
1655
+ }, createMenuItems(subMenu)) : React__default["default"].createElement(uiReact.MenuItem, {
1656
+ key: id,
1657
+ value: id
1658
+ }, name);
1659
+ });
1660
+ };
1661
+
1559
1662
  var getFieldInput = function getFieldInput() {
1560
1663
  var getInternalContent = function getInternalContent() {
1561
1664
  if (props.customInput) {
@@ -1581,7 +1684,7 @@ function FieldRow(props) {
1581
1684
  return React__default["default"].createElement("div", {
1582
1685
  className: "field-row"
1583
1686
  }, React__default["default"].createElement("div", {
1584
- className: "field-selector ".concat(props.required ? 'required-field' : '')
1687
+ className: "field-selector ".concat(props.required && !props.subMenu ? 'required-field' : '')
1585
1688
  }, getFieldSelector()), React__default["default"].createElement("div", {
1586
1689
  className: "field-input-container"
1587
1690
  }, React__default["default"].createElement("div", {
@@ -1657,6 +1760,8 @@ function TaskFields(props) {
1657
1760
  required: field.required,
1658
1761
  fieldName: field.name,
1659
1762
  id: field.id,
1763
+ subMenu: field.subMenu,
1764
+ subMenuLabel: field.subMenuLabel,
1660
1765
  nameTooltip: field.type === CUSTOM_TYPE ? _get__default["default"](props, 'customFieldOptions.nameTooltip', '') : field.nameTooltip,
1661
1766
  valueTooltip: field.type === CUSTOM_TYPE ? _get__default["default"](props, 'customFieldOptions.valueTooltip', '') : field.valueTooltip,
1662
1767
  onFieldNameChange: onFieldNameChange,
@@ -1670,10 +1775,26 @@ function TaskFields(props) {
1670
1775
  customNamePlaceholder: _get__default["default"](props, 'customFieldOptions.namePlaceholder', ''),
1671
1776
  pipedTextItems: props.pipedTextItems,
1672
1777
  searchable: props.searchable
1673
- }, allFieldOptions && availableFields);
1778
+ }, allFieldOptions && getFieldsForFieldRow(availableFields, allFieldOptions, field.id));
1674
1779
  });
1675
1780
  };
1676
1781
 
1782
+ var getFieldsForFieldRow = function getFieldsForFieldRow(allAvailableFields, allFields, fieldId) {
1783
+ var fieldsToAdd = allAvailableFields.slice();
1784
+ allFields.forEach(function (field) {
1785
+ if (field.subMenu !== undefined && fieldId === field.id) {
1786
+ var foundIndex = fieldsToAdd.findIndex(function (fieldToAdd) {
1787
+ return fieldToAdd.id === field.id;
1788
+ });
1789
+
1790
+ if (foundIndex === -1) {
1791
+ fieldsToAdd.push(field);
1792
+ }
1793
+ }
1794
+ });
1795
+ return fieldsToAdd;
1796
+ };
1797
+
1677
1798
  var onFieldAdd = function onFieldAdd(fieldId) {
1678
1799
  if (typeof props.onFieldAdd === 'function') {
1679
1800
  props.onFieldAdd(fieldId);
@@ -1693,7 +1814,9 @@ function TaskFields(props) {
1693
1814
  type: field.type,
1694
1815
  required: field.required,
1695
1816
  key: field.id,
1696
- id: field.id
1817
+ id: field.id,
1818
+ subMenu: field.subMenu,
1819
+ subMenuLabel: field.subMenuLabel
1697
1820
  });
1698
1821
  });
1699
1822
  };
@@ -1746,7 +1869,9 @@ function TaskFields(props) {
1746
1869
  children: foundNextChild.props.children,
1747
1870
  nameTooltip: foundNextChild.props.nameTooltip,
1748
1871
  valueTooltip: foundNextChild.props.valueTooltip,
1749
- valuePlaceholder: foundNextChild.props.valuePlaceholder
1872
+ valuePlaceholder: foundNextChild.props.valuePlaceholder,
1873
+ subMenu: foundNextChild.props.subMenu,
1874
+ subMenuLabel: foundNextChild.props.subMenuLabel
1750
1875
  });
1751
1876
  }
1752
1877
 
@@ -1767,7 +1892,9 @@ function TaskFields(props) {
1767
1892
  nameTooltip: field.props.nameTooltip,
1768
1893
  valueTooltip: field.props.valueTooltip,
1769
1894
  value: field.props.value || undefined,
1770
- valuePlaceholder: field.props.valuePlaceholder
1895
+ valuePlaceholder: field.props.valuePlaceholder,
1896
+ subMenu: field.props.subMenu,
1897
+ subMenuLabel: field.props.subMenuLabel
1771
1898
  });
1772
1899
  }
1773
1900
  });
@@ -1793,7 +1920,9 @@ function TaskFields(props) {
1793
1920
  nameTooltip: _get__default["default"](props, 'customFieldOptions.nameTooltip', ''),
1794
1921
  valueTooltip: _get__default["default"](props, 'customFieldOptions.valueTooltip', ''),
1795
1922
  namePlaceholder: _get__default["default"](props, 'customFieldOptions.namePlaceholder', ''),
1796
- type: CUSTOM_TYPE
1923
+ type: CUSTOM_TYPE,
1924
+ subMenu: currentField.subMenu,
1925
+ subMenuLabel: currentField.subMenuLabel
1797
1926
  };
1798
1927
  nextFieldsInUse.push(customFieldValue);
1799
1928
  }
@@ -1831,7 +1960,9 @@ function TaskFields(props) {
1831
1960
  children: childProps.children,
1832
1961
  nameTooltip: childProps.nameTooltip,
1833
1962
  valueTooltip: childProps.valueTooltip,
1834
- valuePlaceholder: childProps.valuePlaceholder
1963
+ valuePlaceholder: childProps.valuePlaceholder,
1964
+ subMenu: childProps.subMenu,
1965
+ subMenuLabel: childProps.subMenuLabel
1835
1966
  };
1836
1967
  });
1837
1968
  var nextFieldsInUse = getFieldsInUse(nextAllFieldOptions);
@@ -1866,6 +1997,7 @@ function TaskFields(props) {
1866
1997
  }, React__default["default"].createElement(AddFieldButton, {
1867
1998
  label: props.addFieldLabel,
1868
1999
  onFieldAdded: onFieldAdd,
2000
+ disableAddFieldPopover: props.disableAddFieldPopover,
1869
2001
  allowCustom: props.customFieldOptions,
1870
2002
  customLabel: _get__default["default"](props, 'customFieldOptions.label', ''),
1871
2003
  onCustomFieldAdded: props.customFieldOptions ? onCustomFieldAdd : function () {}
@@ -1875,6 +2007,7 @@ function TaskFields(props) {
1875
2007
  function AddFieldButton(props) {
1876
2008
  var label = props.label || '';
1877
2009
  var customLabel = props.customLabel || '';
2010
+ var disableAddFieldPopover = props.disableAddFieldPopover;
1878
2011
 
1879
2012
  var _useState7 = React.useState(false),
1880
2013
  _useState8 = _slicedToArray(_useState7, 2),
@@ -1896,15 +2029,30 @@ function AddFieldButton(props) {
1896
2029
  setPopoverShow(false);
1897
2030
  };
1898
2031
 
2032
+ var children = [];
2033
+
1899
2034
  var onPopoverAnchorClick = function onPopoverAnchorClick() {
1900
- setPopoverShow(!popoverShow);
2035
+ if (!disableAddFieldPopover) {
2036
+ setPopoverShow(!popoverShow);
2037
+ } else {
2038
+ props.onFieldAdded(props.children !== 0 ? getFieldIdWhenPopoverIsClicked(props.children[0].props) : '');
2039
+ }
2040
+ };
2041
+
2042
+ var getFieldIdWhenPopoverIsClicked = function getFieldIdWhenPopoverIsClicked(field) {
2043
+ if (field.subMenu) {
2044
+ return getFieldIdWhenPopoverIsClicked(field.subMenu[0]);
2045
+ } else {
2046
+ return field.id;
2047
+ }
1901
2048
  };
1902
2049
 
1903
- var children = props.children.map(function (child) {
2050
+ children = props.children.map(function (child) {
1904
2051
  return React__default["default"].createElement(FieldItem, {
1905
2052
  name: child.props.name,
1906
2053
  key: child.props.id,
1907
2054
  id: child.props.id,
2055
+ subMenu: child.props.subMenu,
1908
2056
  onClick: function onClick(fieldId) {
1909
2057
  setPopoverShow(false);
1910
2058
  props.onFieldAdded(fieldId);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qualtrics/plugin-ui-react",
3
- "version": "1.3.0",
3
+ "version": "1.3.2-alpha.0",
4
4
  "description": "",
5
5
  "main": "dist/main.js",
6
6
  "module": "dist/main.es.js",
@@ -52,12 +52,12 @@
52
52
  "jest": "^26.6.3",
53
53
  "jest-css-modules": "^2.1.0",
54
54
  "jest-junit": "^12.0.0",
55
- "node-sass": "^5.0.0",
55
+ "node-sass": "^6.0.1",
56
56
  "postcss-preset-env": "^6.7.0",
57
57
  "postcss-reporter": "^7.0.1",
58
58
  "react": "^16.8.0",
59
59
  "react-dom": "^16.8.0",
60
- "react-test-renderer": "^17.0.1",
60
+ "react-test-renderer": "^16.14.0",
61
61
  "rimraf": "^3.0.2",
62
62
  "rollup": "^2.52.2",
63
63
  "rollup-plugin-babel": "^4.3.3",
@@ -75,10 +75,10 @@
75
75
  "typescript": "^4.3.4"
76
76
  },
77
77
  "peerDependencies": {
78
- "react": "^16.8.0",
79
- "react-dom": "^16.8.0",
80
78
  "@qualtrics/base-styles": "^0.2.1",
81
- "@qualtrics/ui-react": "^1.60.0"
79
+ "@qualtrics/ui-react": "^1.60.0",
80
+ "react": "^16.8.0",
81
+ "react-dom": "^16.8.0"
82
82
  },
83
83
  "dependencies": {
84
84
  "@popperjs/core": "^2.5.4",
@@ -1,91 +1,101 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
- <testsuites name="jest tests" tests="36" failures="0" errors="0" time="36.055">
3
- <testsuite name="FieldItem" errors="0" failures="0" skipped="0" timestamp="2021-06-29T23:21:26" time="10.95" tests="2">
2
+ <testsuites name="jest tests" tests="41" failures="0" errors="0" time="17.093">
3
+ <testsuite name="FieldItem" errors="0" failures="0" skipped="0" timestamp="2021-11-15T23:34:01" time="2.989" tests="2">
4
4
  <testcase classname="FieldItem renders properly" name="FieldItem renders properly" time="0.019">
5
5
  </testcase>
6
- <testcase classname="FieldItem invokes onClick handler with expected arguments when clicked" name="FieldItem invokes onClick handler with expected arguments when clicked" time="0.046">
6
+ <testcase classname="FieldItem invokes onClick handler with expected arguments when clicked" name="FieldItem invokes onClick handler with expected arguments when clicked" time="0.048">
7
7
  </testcase>
8
8
  </testsuite>
9
- <testsuite name="SelectItem" errors="0" failures="0" skipped="0" timestamp="2021-06-29T23:21:26" time="13.889" tests="1">
10
- <testcase classname="SelectItem fires the onChange handler with expected args" name="SelectItem fires the onChange handler with expected args" time="0.048">
9
+ <testsuite name="SelectItem" errors="0" failures="0" skipped="0" timestamp="2021-11-15T23:34:01" time="3.927" tests="1">
10
+ <testcase classname="SelectItem fires the onChange handler with expected args" name="SelectItem fires the onChange handler with expected args" time="0.051">
11
11
  </testcase>
12
12
  </testsuite>
13
- <testsuite name="DynamicSearchInput" errors="0" failures="0" skipped="0" timestamp="2021-06-29T23:21:26" time="15.874" tests="1">
14
- <testcase classname="DynamicSearchInput renders correctly" name="DynamicSearchInput renders correctly" time="0.002">
13
+ <testsuite name="DynamicSearchInput" errors="0" failures="0" skipped="0" timestamp="2021-11-15T23:34:01" time="4.657" tests="1">
14
+ <testcase classname="DynamicSearchInput renders correctly" name="DynamicSearchInput renders correctly" time="0.001">
15
15
  </testcase>
16
16
  </testsuite>
17
- <testsuite name="FieldRow" errors="0" failures="0" skipped="0" timestamp="2021-06-29T23:21:26" time="16.197" tests="1">
18
- <testcase classname="FieldRow fires the onFieldNameChange handler when field name is changed" name="FieldRow fires the onFieldNameChange handler when field name is changed" time="0.063">
17
+ <testsuite name="MultiselectDropdown" errors="0" failures="0" skipped="0" timestamp="2021-11-15T23:34:01" time="5.095" tests="3">
18
+ <testcase classname="MultiselectDropdown toggles dropdown when button is clicked" name="MultiselectDropdown toggles dropdown when button is clicked" time="0.228">
19
+ </testcase>
20
+ <testcase classname="MultiselectDropdown calls onChange handler when item is clicked" name="MultiselectDropdown calls onChange handler when item is clicked" time="0.186">
21
+ </testcase>
22
+ <testcase classname="MultiselectDropdown closes menu on click away" name="MultiselectDropdown closes menu on click away" time="0.051">
19
23
  </testcase>
20
24
  </testsuite>
21
- <testsuite name="MultiselectDropdown" errors="0" failures="0" skipped="0" timestamp="2021-06-29T23:21:26" time="17.095" tests="3">
22
- <testcase classname="MultiselectDropdown toggles dropdown when button is clicked" name="MultiselectDropdown toggles dropdown when button is clicked" time="0.276">
25
+ <testsuite name="FieldRow" errors="0" failures="0" skipped="0" timestamp="2021-11-15T23:34:01" time="5.164" tests="6">
26
+ <testcase classname="FieldRow fires the onFieldNameChange handler when field name is changed" name="FieldRow fires the onFieldNameChange handler when field name is changed" time="0.062">
23
27
  </testcase>
24
- <testcase classname="MultiselectDropdown calls onChange handler when item is clicked" name="MultiselectDropdown calls onChange handler when item is clicked" time="0.705">
28
+ <testcase classname="FieldRow fires the onFieldValueChange handler when field&apos;s value is changed" name="FieldRow fires the onFieldValueChange handler when field&apos;s value is changed" time="0.031">
25
29
  </testcase>
26
- <testcase classname="MultiselectDropdown closes menu on click away" name="MultiselectDropdown closes menu on click away" time="0.109">
30
+ <testcase classname="FieldRow fires the onFieldSwitch handler when field is switched to different field type" name="FieldRow fires the onFieldSwitch handler when field is switched to different field type" time="0.054">
27
31
  </testcase>
28
- </testsuite>
29
- <testsuite name="PipedTextInput" errors="0" failures="0" skipped="0" timestamp="2021-06-29T23:21:26" time="17.619" tests="6">
30
- <testcase classname="PipedTextInput toggles menu open/closed on click" name="PipedTextInput toggles menu open/closed on click" time="0.372">
32
+ <testcase classname="FieldRow fires the onRemove handler when the delete button is clicked" name="FieldRow fires the onRemove handler when the delete button is clicked" time="0.026">
31
33
  </testcase>
32
- <testcase classname="PipedTextInput fires onChange handler when input value changes from selecting piped text" name="PipedTextInput fires onChange handler when input value changes from selecting piped text" time="0.09">
34
+ <testcase classname="FieldRow should allow for searching in field row input dropdown when searchable field provided" name="FieldRow should allow for searching in field row input dropdown when searchable field provided" time="0.205">
33
35
  </testcase>
34
- <testcase classname="PipedTextInput fires onChange handler when input value changes from user typing in input box" name="PipedTextInput fires onChange handler when input value changes from user typing in input box" time="0.008">
36
+ <testcase classname="FieldRow should not allow searching in field row input dropdown when searchable is false" name="FieldRow should not allow searching in field row input dropdown when searchable is false" time="0.022">
35
37
  </testcase>
36
- <testcase classname="PipedTextInput fires onSelect handler when piped text is selected" name="PipedTextInput fires onSelect handler when piped text is selected" time="0.077">
38
+ </testsuite>
39
+ <testsuite name="TaskFields" errors="0" failures="0" skipped="0" timestamp="2021-11-15T23:34:01" time="5.336" tests="4">
40
+ <testcase classname="TaskFields only renders custom fields if custom field configuration options provided" name="TaskFields only renders custom fields if custom field configuration options provided" time="0.441">
37
41
  </testcase>
38
- <testcase classname="PipedTextInput fires onClose handler when menu is closed" name="PipedTextInput fires onClose handler when menu is closed" time="0.074">
42
+ <testcase classname="TaskFields fires onFieldNameChange handler when a custom field&apos;s name is changed" name="TaskFields fires onFieldNameChange handler when a custom field&apos;s name is changed" time="0.034">
39
43
  </testcase>
40
- <testcase classname="PipedTextInput inserts piped text at the position of the cursor in the input box when piped text is selected" name="PipedTextInput inserts piped text at the position of the cursor in the input box when piped text is selected" time="0.111">
44
+ <testcase classname="TaskFields fires onFieldAdd handler when a field is added" name="TaskFields fires onFieldAdd handler when a field is added" time="0.056">
45
+ </testcase>
46
+ <testcase classname="TaskFields fires onCustomFieldAdd handler when a custom field is added" name="TaskFields fires onCustomFieldAdd handler when a custom field is added" time="0.047">
41
47
  </testcase>
42
48
  </testsuite>
43
- <testsuite name="TaskFields" errors="0" failures="0" skipped="0" timestamp="2021-06-29T23:21:26" time="18.071" tests="4">
44
- <testcase classname="TaskFields only renders custom fields if custom field configuration options provided" name="TaskFields only renders custom fields if custom field configuration options provided" time="1.013">
49
+ <testsuite name="PipedTextInput" errors="0" failures="0" skipped="0" timestamp="2021-11-15T23:34:01" time="5.506" tests="6">
50
+ <testcase classname="PipedTextInput toggles menu open/closed on click" name="PipedTextInput toggles menu open/closed on click" time="0.352">
51
+ </testcase>
52
+ <testcase classname="PipedTextInput fires onChange handler when input value changes from selecting piped text" name="PipedTextInput fires onChange handler when input value changes from selecting piped text" time="0.117">
53
+ </testcase>
54
+ <testcase classname="PipedTextInput fires onChange handler when input value changes from user typing in input box" name="PipedTextInput fires onChange handler when input value changes from user typing in input box" time="0.01">
45
55
  </testcase>
46
- <testcase classname="TaskFields fires onFieldNameChange handler when a custom field&apos;s name is changed" name="TaskFields fires onFieldNameChange handler when a custom field&apos;s name is changed" time="0.033">
56
+ <testcase classname="PipedTextInput fires onSelect handler when piped text is selected" name="PipedTextInput fires onSelect handler when piped text is selected" time="0.097">
47
57
  </testcase>
48
- <testcase classname="TaskFields fires onFieldAdd handler when a field is added" name="TaskFields fires onFieldAdd handler when a field is added" time="0.053">
58
+ <testcase classname="PipedTextInput fires onClose handler when menu is closed" name="PipedTextInput fires onClose handler when menu is closed" time="0.086">
49
59
  </testcase>
50
- <testcase classname="TaskFields fires onCustomFieldAdd handler when a custom field is added" name="TaskFields fires onCustomFieldAdd handler when a custom field is added" time="0.058">
60
+ <testcase classname="PipedTextInput inserts piped text at the position of the cursor in the input box when piped text is selected" name="PipedTextInput inserts piped text at the position of the cursor in the input box when piped text is selected" time="0.086">
51
61
  </testcase>
52
62
  </testsuite>
53
- <testsuite name="PipedTextSelect" errors="0" failures="0" skipped="0" timestamp="2021-06-29T23:21:26" time="26.879" tests="18">
54
- <testcase classname="PipedTextSelect filters menu items to items containing the search value as text" name="PipedTextSelect filters menu items to items containing the search value as text" time="1.028">
63
+ <testsuite name="PipedTextSelect" errors="0" failures="0" skipped="0" timestamp="2021-11-15T23:34:01" time="13.594" tests="18">
64
+ <testcase classname="PipedTextSelect filters menu items to items containing the search value as text" name="PipedTextSelect filters menu items to items containing the search value as text" time="0.383">
55
65
  </testcase>
56
- <testcase classname="PipedTextSelect filters menu items to items containing the search value as a substring" name="PipedTextSelect filters menu items to items containing the search value as a substring" time="0.174">
66
+ <testcase classname="PipedTextSelect filters menu items to items containing the search value as a substring" name="PipedTextSelect filters menu items to items containing the search value as a substring" time="0.17">
57
67
  </testcase>
58
- <testcase classname="PipedTextSelect filters menu items with special characters to items containing the search value" name="PipedTextSelect filters menu items with special characters to items containing the search value" time="0.152">
68
+ <testcase classname="PipedTextSelect filters menu items with special characters to items containing the search value" name="PipedTextSelect filters menu items with special characters to items containing the search value" time="0.169">
59
69
  </testcase>
60
- <testcase classname="PipedTextSelect renders submenu correctly when hovering over a submenu item" name="PipedTextSelect renders submenu correctly when hovering over a submenu item" time="0.554">
70
+ <testcase classname="PipedTextSelect renders submenu correctly when hovering over a submenu item" name="PipedTextSelect renders submenu correctly when hovering over a submenu item" time="0.627">
61
71
  </testcase>
62
- <testcase classname="PipedTextSelect hides other submenus when hovering over another submenu" name="PipedTextSelect hides other submenus when hovering over another submenu" time="0.618">
72
+ <testcase classname="PipedTextSelect hides other submenus when hovering over another submenu" name="PipedTextSelect hides other submenus when hovering over another submenu" time="0.714">
63
73
  </testcase>
64
- <testcase classname="PipedTextSelect hides other submenus when hovering over another item" name="PipedTextSelect hides other submenus when hovering over another item" time="0.387">
74
+ <testcase classname="PipedTextSelect hides other submenus when hovering over another item" name="PipedTextSelect hides other submenus when hovering over another item" time="0.283">
65
75
  </testcase>
66
- <testcase classname="PipedTextSelect fires onSelect handler when a menu item is selected" name="PipedTextSelect fires onSelect handler when a menu item is selected" time="0.148">
76
+ <testcase classname="PipedTextSelect fires onSelect handler when a menu item is selected" name="PipedTextSelect fires onSelect handler when a menu item is selected" time="0.219">
67
77
  </testcase>
68
- <testcase classname="PipedTextSelect doesn&apos;t fire onSelect handler when a disabled menu item is selected" name="PipedTextSelect doesn&apos;t fire onSelect handler when a disabled menu item is selected" time="0.146">
78
+ <testcase classname="PipedTextSelect doesn&apos;t fire onSelect handler when a disabled menu item is selected" name="PipedTextSelect doesn&apos;t fire onSelect handler when a disabled menu item is selected" time="0.174">
69
79
  </testcase>
70
- <testcase classname="PipedTextSelect fires onClose handler when menu item selected" name="PipedTextSelect fires onClose handler when menu item selected" time="0.148">
80
+ <testcase classname="PipedTextSelect fires onClose handler when menu item selected" name="PipedTextSelect fires onClose handler when menu item selected" time="0.151">
71
81
  </testcase>
72
- <testcase classname="PipedTextSelect fires onClose handler when nested item selected" name="PipedTextSelect fires onClose handler when nested item selected" time="1.37">
82
+ <testcase classname="PipedTextSelect fires onClose handler when nested item selected" name="PipedTextSelect fires onClose handler when nested item selected" time="1.345">
73
83
  </testcase>
74
- <testcase classname="PipedTextSelect fires onClose handler when clicking outside of the menu" name="PipedTextSelect fires onClose handler when clicking outside of the menu" time="0.207">
84
+ <testcase classname="PipedTextSelect fires onClose handler when clicking outside of the menu" name="PipedTextSelect fires onClose handler when clicking outside of the menu" time="0.044">
75
85
  </testcase>
76
- <testcase classname="PipedTextSelect displays the custom input message if none of the menu items match the search query and custom input is enabled" name="PipedTextSelect displays the custom input message if none of the menu items match the search query and custom input is enabled" time="0.626">
86
+ <testcase classname="PipedTextSelect displays the custom input message if none of the menu items match the search query and custom input is enabled" name="PipedTextSelect displays the custom input message if none of the menu items match the search query and custom input is enabled" time="0.578">
77
87
  </testcase>
78
- <testcase classname="PipedTextSelect correctly creates a custom locator based on the provided scheme when the user hits the &quot;enter&quot; in the search query" name="PipedTextSelect correctly creates a custom locator based on the provided scheme when the user hits the &quot;enter&quot; in the search query" time="0.541">
88
+ <testcase classname="PipedTextSelect correctly creates a custom locator based on the provided scheme when the user hits the &quot;enter&quot; in the search query" name="PipedTextSelect correctly creates a custom locator based on the provided scheme when the user hits the &quot;enter&quot; in the search query" time="0.558">
79
89
  </testcase>
80
- <testcase classname="PipedTextSelect calls the onClose handler when custom input is enabled and the user hits &quot;enter&quot; in the search query" name="PipedTextSelect calls the onClose handler when custom input is enabled and the user hits &quot;enter&quot; in the search query" time="0.565">
90
+ <testcase classname="PipedTextSelect calls the onClose handler when custom input is enabled and the user hits &quot;enter&quot; in the search query" name="PipedTextSelect calls the onClose handler when custom input is enabled and the user hits &quot;enter&quot; in the search query" time="0.527">
81
91
  </testcase>
82
- <testcase classname="PipedTextSelect does not call the onClose handler when custom input is disabled and the user hits &quot;enter&quot; in the search query" name="PipedTextSelect does not call the onClose handler when custom input is disabled and the user hits &quot;enter&quot; in the search query" time="0.651">
92
+ <testcase classname="PipedTextSelect does not call the onClose handler when custom input is disabled and the user hits &quot;enter&quot; in the search query" name="PipedTextSelect does not call the onClose handler when custom input is disabled and the user hits &quot;enter&quot; in the search query" time="0.704">
83
93
  </testcase>
84
- <testcase classname="PipedTextSelect displays piped text items provided by an async &quot;items&quot; function" name="PipedTextSelect displays piped text items provided by an async &quot;items&quot; function" time="0.555">
94
+ <testcase classname="PipedTextSelect displays piped text items provided by an async &quot;items&quot; function" name="PipedTextSelect displays piped text items provided by an async &quot;items&quot; function" time="0.635">
85
95
  </testcase>
86
- <testcase classname="PipedTextSelect displays piped text items provided by a non-async &quot;items&quot; function" name="PipedTextSelect displays piped text items provided by a non-async &quot;items&quot; function" time="0.547">
96
+ <testcase classname="PipedTextSelect displays piped text items provided by a non-async &quot;items&quot; function" name="PipedTextSelect displays piped text items provided by a non-async &quot;items&quot; function" time="0.588">
87
97
  </testcase>
88
- <testcase classname="PipedTextSelect displays loading error message if async piped text items function fails to resolve" name="PipedTextSelect displays loading error message if async piped text items function fails to resolve" time="0.557">
98
+ <testcase classname="PipedTextSelect displays loading error message if async piped text items function fails to resolve" name="PipedTextSelect displays loading error message if async piped text items function fails to resolve" time="0.615">
89
99
  </testcase>
90
100
  </testsuite>
91
101
  </testsuites>