@elliemae/ds-app-picker 2.0.0-alpha.1 → 2.0.0-alpha.13

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,163 +1,155 @@
1
1
  'use strict';
2
2
 
3
3
  var _jsx = require('@babel/runtime/helpers/jsx');
4
+ require('core-js/modules/esnext.async-iterator.for-each.js');
5
+ require('core-js/modules/esnext.iterator.constructor.js');
6
+ require('core-js/modules/esnext.iterator.for-each.js');
7
+ require('core-js/modules/esnext.async-iterator.map.js');
8
+ require('core-js/modules/esnext.iterator.map.js');
4
9
  var react = require('react');
5
10
  var lodash = require('lodash');
6
- var dsClassnames = require('@elliemae/ds-classnames');
7
11
  var utils = require('./utils.js');
12
+ var styles = require('./styles.js');
8
13
  var jsxRuntime = require('react/jsx-runtime');
9
14
 
10
15
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
11
16
 
12
17
  var _jsx__default = /*#__PURE__*/_interopDefaultLegacy(_jsx);
13
18
 
14
- var _Separator;
15
- const blockName = 'app-picker';
16
- const Wrapper = dsClassnames.aggregatedClasses('div')(blockName, 'wrapper');
17
- const Grid = dsClassnames.aggregatedClasses('div')(blockName, 'grid');
18
- const Row = dsClassnames.aggregatedClasses('div')(blockName, 'row');
19
- const Chip = dsClassnames.aggregatedClasses('div')(blockName, 'chip', ({
20
- disabled
21
- }) => ({
22
- [disabled]: disabled
23
- }));
24
- const ChipLabel = dsClassnames.aggregatedClasses('p')(blockName, 'chip-label');
25
- const Separator = dsClassnames.aggregatedClasses('hr')(blockName, 'separator');
26
- const SectionTitle = dsClassnames.aggregatedClasses('p')(blockName, 'section-title');
27
-
28
- const AppPickerImpl = ({
29
- apps = [],
30
- customApps = [],
31
- sectionTitle = 'APPLICATIONS',
32
- customSectionTitle = 'CUSTOM APPLICATIONS',
33
- close = () => null
34
- }) => {
35
- const wrapperRef = react.useRef();
19
+ var _StyledSeparator;
20
+
21
+ const AppPickerImpl = _ref => {
22
+ let {
23
+ apps = [],
24
+ customApps = [],
25
+ sectionTitle = 'APPLICATIONS',
26
+ customSectionTitle = 'CUSTOM APPLICATIONS',
27
+ close = () => null,
28
+ wrapperRef,
29
+ onKeyDown,
30
+ triggerRef
31
+ } = _ref;
32
+ const allFocusableButtons = react.useRef([]);
33
+ const selectedButton = react.useRef(null);
36
34
  react.useEffect(() => {
37
35
  var _wrapperRef$current;
38
36
 
39
- wrapperRef === null || wrapperRef === void 0 ? void 0 : (_wrapperRef$current = wrapperRef.current) === null || _wrapperRef$current === void 0 ? void 0 : _wrapperRef$current.focus();
40
- }, []);
37
+ wrapperRef === null || wrapperRef === void 0 ? void 0 : (_wrapperRef$current = wrapperRef.current) === null || _wrapperRef$current === void 0 ? void 0 : _wrapperRef$current.querySelectorAll('button').forEach((e, index) => {
38
+ if (!e.hasAttribute('disabled')) {
39
+ var _allFocusableButtons$;
41
40
 
42
- const handleKeyDown = ({
43
- target: element,
44
- which: key
45
- }) => {
46
- var _upRow$children$index, _downRow$children$ind;
47
-
48
- const parent = element.parentElement;
49
- const index = [...parent.children].indexOf(element);
50
- const {
51
- previousElementSibling,
52
- nextElementSibling
53
- } = element;
54
- const {
55
- previousElementSibling: upRow,
56
- nextElementSibling: downRow
57
- } = parent;
58
-
59
- switch (key) {
60
- case utils.keys.LEFT:
61
- if (previousElementSibling) {
62
- previousElementSibling.focus();
63
- } else if (upRow) {
64
- upRow.lastChild.focus();
65
- }
41
+ allFocusableButtons === null || allFocusableButtons === void 0 ? void 0 : (_allFocusableButtons$ = allFocusableButtons.current) === null || _allFocusableButtons$ === void 0 ? void 0 : _allFocusableButtons$.push(e);
42
+ }
66
43
 
67
- break;
68
-
69
- case utils.keys.RIGHT:
70
- if (nextElementSibling) {
71
- nextElementSibling.focus();
72
- } else if (downRow) {
73
- downRow.firstChild.focus();
74
- }
44
+ if (e.getAttribute('aria-selected') === 'true') {
45
+ selectedButton.current = index;
46
+ }
47
+ });
75
48
 
76
- break;
49
+ if (selectedButton.current) {
50
+ var _wrapperRef$current2;
77
51
 
78
- case utils.keys.UP:
79
- upRow === null || upRow === void 0 ? void 0 : (_upRow$children$index = upRow.children[index]) === null || _upRow$children$index === void 0 ? void 0 : _upRow$children$index.focus();
80
- break;
52
+ wrapperRef === null || wrapperRef === void 0 ? void 0 : (_wrapperRef$current2 = wrapperRef.current) === null || _wrapperRef$current2 === void 0 ? void 0 : _wrapperRef$current2.querySelectorAll('button')[selectedButton.current].focus();
53
+ } else {
54
+ var _allFocusableButtons$2;
81
55
 
82
- case utils.keys.DOWN:
83
- downRow === null || downRow === void 0 ? void 0 : (_downRow$children$ind = downRow.children[index]) === null || _downRow$children$ind === void 0 ? void 0 : _downRow$children$ind.focus();
84
- break;
56
+ allFocusableButtons === null || allFocusableButtons === void 0 ? void 0 : (_allFocusableButtons$2 = allFocusableButtons.current[0]) === null || _allFocusableButtons$2 === void 0 ? void 0 : _allFocusableButtons$2.focus();
57
+ }
58
+ }, [wrapperRef]); // eslint-disable-next-line max-statements
85
59
 
86
- case utils.keys.ENTER:
87
- case utils.keys.SPACE:
88
- element.click();
89
- break;
60
+ const handleKeyDown = e => {
61
+ var _triggerRef$current;
90
62
 
63
+ switch (e.key) {
91
64
  case utils.keys.ESC:
65
+ triggerRef === null || triggerRef === void 0 ? void 0 : (_triggerRef$current = triggerRef.current) === null || _triggerRef$current === void 0 ? void 0 : _triggerRef$current.focus();
92
66
  close();
93
67
  break;
94
- }
95
- };
96
68
 
97
- const handleOnClick = (e, app) => {
98
- if (!app.disabled) {
99
- close();
100
- app.onClick(e);
69
+ case utils.keys.TAB:
70
+ if (e.shiftKey) {
71
+ if (e.target === allFocusableButtons.current[0]) {
72
+ var _allFocusableButtons$3;
73
+
74
+ e.preventDefault();
75
+ allFocusableButtons === null || allFocusableButtons === void 0 ? void 0 : (_allFocusableButtons$3 = allFocusableButtons.current[allFocusableButtons.current.length - 1]) === null || _allFocusableButtons$3 === void 0 ? void 0 : _allFocusableButtons$3.focus();
76
+ }
77
+ } else if (e.target === allFocusableButtons.current[allFocusableButtons.current.length - 1]) {
78
+ var _allFocusableButtons$4;
79
+
80
+ e.preventDefault();
81
+ allFocusableButtons === null || allFocusableButtons === void 0 ? void 0 : (_allFocusableButtons$4 = allFocusableButtons.current[0]) === null || _allFocusableButtons$4 === void 0 ? void 0 : _allFocusableButtons$4.focus();
82
+ }
83
+
84
+ break;
101
85
  }
102
86
  };
103
87
 
104
- const handleOnHover = ({
105
- target
106
- }) => target.focus();
88
+ const handleOnClick = react.useCallback((e, app) => {
89
+ if (app.onClick) app.onClick(e, app);
90
+ }, []);
91
+ const handleOnKeyDownWrapper = react.useCallback(e => {
92
+ if (onKeyDown) onKeyDown(e);
107
93
 
108
- const handleOnKeyDownWrapper = ({
109
- key
110
- }) => {
111
- if (key === utils.keys.ESC) {
94
+ if (!onKeyDown && e.key === utils.keys.ESC) {
112
95
  close();
113
96
  }
114
- };
97
+ }, [onKeyDown, close]);
115
98
 
116
- const buildRows = appList => {
99
+ const buildRows = function (appList) {
100
+ let prevIndex = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
117
101
  const rows = lodash.chunk(appList, 3); // divides array in subarrays of 3 items
118
102
 
119
- const formattedRows = rows.map((row, index) => /*#__PURE__*/_jsx__default['default'](Row, {}, index, row.map((app, key) => {
103
+ const formattedRows = rows.map((row, index) => /*#__PURE__*/_jsx__default["default"](styles.StyledRow, {}, index, row.map((app, key) => {
120
104
  const {
121
105
  label,
122
106
  disabled,
107
+ selected,
123
108
  icon: Icon
124
109
  } = app;
125
- return /*#__PURE__*/_jsx__default['default'](Chip, {
126
- classProps: {
127
- disabled
128
- },
110
+ return /*#__PURE__*/_jsx__default["default"](styles.StyledChip, {
129
111
  onClick: e => handleOnClick(e, app),
130
112
  onKeyDown: handleKeyDown,
131
- onMouseEnter: handleOnHover,
132
- tabIndex: 0,
133
- "data-testid": "app-picker__chip"
134
- }, key, /*#__PURE__*/_jsx__default['default'](Icon, {
113
+ "data-testid": "app-picker__chip",
114
+ "aria-disabled": disabled,
115
+ disabled: disabled,
116
+ selected: selected,
117
+ "aria-selected": selected,
118
+ "aria-setsize": apps.length + customApps.length,
119
+ "aria-posinset": key + prevIndex
120
+ }, key, /*#__PURE__*/_jsx__default["default"](Icon, {
135
121
  className: "app-picker__icon",
136
122
  size: "m"
137
- }), /*#__PURE__*/_jsx__default['default'](ChipLabel, {}, void 0, label));
123
+ }), /*#__PURE__*/_jsx__default["default"](styles.StyledChipLabel, {}, void 0, label));
138
124
  })));
139
- return formattedRows;
125
+ return /*#__PURE__*/jsxRuntime.jsx(jsxRuntime.Fragment, {
126
+ children: formattedRows
127
+ });
140
128
  };
141
129
 
142
- const AppsRows = () => buildRows(apps);
130
+ const AppsRows = () => buildRows(apps, 1);
143
131
 
144
- const CustomRows = () => buildRows(customApps);
132
+ const CustomRows = () => buildRows(customApps, apps.length);
145
133
 
146
- return /*#__PURE__*/jsxRuntime.jsxs(Wrapper, {
134
+ return /*#__PURE__*/jsxRuntime.jsxs(styles.StyledWrapper, {
135
+ role: "listbox",
147
136
  ref: wrapperRef,
148
- tabIndex: 0,
149
137
  onKeyDown: handleOnKeyDownWrapper,
150
138
  "data-testid": "app-picker__wrapper",
151
- children: [/*#__PURE__*/_jsx__default['default'](SectionTitle, {
152
- "data-testid": "app-picker__main-title"
153
- }, void 0, sectionTitle), /*#__PURE__*/_jsx__default['default'](Grid, {
139
+ children: [/*#__PURE__*/_jsx__default["default"](styles.StyledTitle, {
140
+ "data-testid": "app-picker__main-title",
141
+ "aria-hidden": true
142
+ }, void 0, sectionTitle), /*#__PURE__*/_jsx__default["default"](styles.StyledGrid, {
154
143
  "data-testid": "app-picker__main-grid"
155
- }, void 0, /*#__PURE__*/_jsx__default['default'](AppsRows, {})), !!customApps.length && /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
156
- children: [_Separator || (_Separator = /*#__PURE__*/_jsx__default['default'](Separator, {})), /*#__PURE__*/_jsx__default['default'](SectionTitle, {
157
- "data-testid": "app-picker__custom-title"
158
- }, void 0, customSectionTitle), /*#__PURE__*/_jsx__default['default'](Grid, {
144
+ }, void 0, /*#__PURE__*/_jsx__default["default"](AppsRows, {})), !!customApps.length && /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
145
+ children: [_StyledSeparator || (_StyledSeparator = /*#__PURE__*/_jsx__default["default"](styles.StyledSeparator, {
146
+ "aria-hidden": true
147
+ })), /*#__PURE__*/_jsx__default["default"](styles.StyledTitle, {
148
+ "data-testid": "app-picker__custom-title",
149
+ "aria-hidden": true
150
+ }, void 0, customSectionTitle), /*#__PURE__*/_jsx__default["default"](styles.StyledGrid, {
159
151
  "data-testid": "app-picker__custom-grid"
160
- }, void 0, /*#__PURE__*/_jsx__default['default'](CustomRows, {}))]
152
+ }, void 0, /*#__PURE__*/_jsx__default["default"](CustomRows, {}))]
161
153
  })]
162
154
  });
163
155
  };
@@ -3,66 +3,106 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var _jsx = require('@babel/runtime/helpers/jsx');
6
+ require('core-js/modules/web.dom-collections.iterator.js');
6
7
  var react = require('react');
7
8
  var reactDesc = require('react-desc');
8
- var MenuPicker = require('@elliemae/ds-icons/MenuPicker');
9
+ var dsIcons = require('@elliemae/ds-icons');
9
10
  var DSButton = require('@elliemae/ds-button');
10
11
  var DSPopover = require('@elliemae/ds-popover');
12
+ var dsUtilities = require('@elliemae/ds-utilities');
11
13
  var AppPickerImpl = require('./AppPickerImpl.js');
14
+ var propTypes = require('./propTypes.js');
12
15
 
13
16
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
14
17
 
15
18
  var _jsx__default = /*#__PURE__*/_interopDefaultLegacy(_jsx);
16
- var MenuPicker__default = /*#__PURE__*/_interopDefaultLegacy(MenuPicker);
17
19
  var DSButton__default = /*#__PURE__*/_interopDefaultLegacy(DSButton);
18
20
  var DSPopover__default = /*#__PURE__*/_interopDefaultLegacy(DSPopover);
19
21
 
20
- const DSAppPicker = ({
21
- apps = [],
22
- customApps = [],
23
- sectionTitle = 'APPLICATIONS',
24
- customSectionTitle = 'CUSTOM APPLICATIONS',
25
- icon: Icon = () => /*#__PURE__*/_jsx__default['default'](MenuPicker__default['default'], {
26
- fill: ['brand-primary', 700],
27
- size: "m"
28
- }),
29
- renderTrigger,
30
- isOpen = false,
31
- onClose = () => null
32
- }) => {
33
- var _AppPickerImpl, _Icon;
22
+ const DSAppPicker = _ref => {
23
+ var _Icon;
34
24
 
35
- const [open, setOpen] = react.useState(isOpen);
36
- react.useEffect(() => setOpen(isOpen), [isOpen]);
25
+ let {
26
+ apps = [],
27
+ customApps = [],
28
+ sectionTitle = 'APPLICATIONS',
29
+ customSectionTitle = 'CUSTOM APPLICATIONS',
30
+ icon: Icon = () => /*#__PURE__*/_jsx__default["default"](dsIcons.MenuPicker, {
31
+ fill: ['brand-primary', 700],
32
+ size: "m"
33
+ }),
34
+ renderTrigger,
35
+ isOpen,
36
+ onClose = () => null,
37
+ actionRef,
38
+ onKeyDown,
39
+ onClick = () => null,
40
+ onClickOutside = () => null,
41
+ triggerRef
42
+ } = _ref;
43
+ const [open, setOpen] = react.useState(false);
44
+ const wrapperRef = react.useRef(null);
45
+ const defaultTriggerRef = react.useRef(null);
46
+ react.useEffect(() => {
47
+ if (actionRef && actionRef.current) {
48
+ actionRef.current.focusToIndex = index => {
49
+ if (wrapperRef.current) {
50
+ const parent = wrapperRef.current;
51
+ const buttons = [...parent.querySelectorAll('button')];
52
+ buttons[index].focus();
53
+ }
54
+ };
55
+ }
56
+ }, [actionRef, apps, customApps]);
37
57
 
38
58
  const handleOnClose = () => {
59
+ if (typeof isOpen === 'boolean') {
60
+ setOpen(isOpen);
61
+ } else {
62
+ setOpen(false);
63
+ }
64
+
65
+ onClose();
66
+ };
67
+
68
+ const handleOnClickOutside = e => {
39
69
  setOpen(false);
40
70
  onClose();
71
+ onClickOutside(e);
41
72
  };
42
73
 
43
- const AppPickerContent = () => _AppPickerImpl || (_AppPickerImpl = /*#__PURE__*/_jsx__default['default'](AppPickerImpl, {
74
+ const AppPickerContent = () => /*#__PURE__*/_jsx__default["default"](AppPickerImpl, {
44
75
  apps: apps,
45
76
  customApps: customApps,
46
77
  sectionTitle: sectionTitle,
47
78
  customSectionTitle: customSectionTitle,
48
- close: handleOnClose
49
- }));
79
+ close: handleOnClose,
80
+ wrapperRef: wrapperRef,
81
+ onKeyDown: onKeyDown,
82
+ triggerRef: triggerRef || defaultTriggerRef
83
+ });
50
84
 
51
- const RenderTrigger = renderTrigger || (({
52
- ref
53
- }) => /*#__PURE__*/_jsx__default['default'](DSButton__default['default'], {
54
- "data-testid": "app-picker__button",
55
- id: "app-picker__button",
56
- buttonType: "text",
57
- icon: _Icon || (_Icon = /*#__PURE__*/_jsx__default['default'](Icon, {})),
58
- innerRef: ref,
59
- onClick: () => setOpen(true)
60
- }));
85
+ const RenderTrigger = renderTrigger || (_ref2 => {
86
+ let {
87
+ ref
88
+ } = _ref2;
89
+ return /*#__PURE__*/_jsx__default["default"](DSButton__default["default"], {
90
+ "data-testid": "app-picker__button",
91
+ id: "app-picker__button",
92
+ buttonType: "text",
93
+ icon: _Icon || (_Icon = /*#__PURE__*/_jsx__default["default"](Icon, {})),
94
+ innerRef: dsUtilities.mergeRefs(ref, defaultTriggerRef),
95
+ onClick: e => {
96
+ onClick(e);
97
+ setOpen(true);
98
+ }
99
+ });
100
+ });
61
101
 
62
- return /*#__PURE__*/_jsx__default['default'](DSPopover__default['default'], {
63
- content: /*#__PURE__*/_jsx__default['default'](AppPickerContent, {}),
64
- isOpen: open,
65
- onClickOutside: handleOnClose,
102
+ return /*#__PURE__*/_jsx__default["default"](DSPopover__default["default"], {
103
+ content: /*#__PURE__*/_jsx__default["default"](AppPickerContent, {}),
104
+ isOpen: typeof isOpen === 'boolean' ? isOpen : open,
105
+ onClickOutside: handleOnClickOutside,
66
106
  placement: "bottom",
67
107
  interactionType: "click",
68
108
  renderTrigger: RenderTrigger,
@@ -74,16 +114,9 @@ const DSAppPicker = ({
74
114
  });
75
115
  };
76
116
 
77
- const appPickerProps = {
78
- apps: reactDesc.PropTypes.array.description('Main items. Format: [{ label:string, icon:component, onClick:func, disabled:bool }]').isRequired,
79
- customApps: reactDesc.PropTypes.array.description('Custom items. Format: [{ label:string, icon:component, onClick:func, disabled:bool }]'),
80
- sectionTitle: reactDesc.PropTypes.string.description('main section title').defaultValue('APPLICATIONS'),
81
- customSectionTitle: reactDesc.PropTypes.string.description('custom section title').defaultValue('CUSTOM APPLICATIONS'),
82
- icon: reactDesc.PropTypes.func.description('trigger button s icon').defaultValue(MenuPicker__default['default'])
83
- };
84
117
  const AppPickerWithSchema = reactDesc.describe(DSAppPicker);
85
- AppPickerWithSchema.propTypes = appPickerProps;
118
+ AppPickerWithSchema.propTypes = propTypes.propTypes;
86
119
 
87
120
  exports.AppPickerWithSchema = AppPickerWithSchema;
88
121
  exports.DSAppPicker = DSAppPicker;
89
- exports['default'] = DSAppPicker;
122
+ exports["default"] = DSAppPicker;
package/cjs/index.js CHANGED
@@ -8,4 +8,4 @@ var DSAppPicker = require('./DSAppPicker.js');
8
8
 
9
9
  exports.AppPickerWithSchema = DSAppPicker.AppPickerWithSchema;
10
10
  exports.DSAppPicker = DSAppPicker.DSAppPicker;
11
- exports.default = DSAppPicker.DSAppPicker;
11
+ exports["default"] = DSAppPicker.DSAppPicker;
@@ -0,0 +1,25 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var reactDesc = require('react-desc');
6
+ var dsIcons = require('@elliemae/ds-icons');
7
+
8
+ const propTypes = {
9
+ apps: reactDesc.PropTypes.array.description('Main items. Format: [{ label:string, icon:component, onClick:func, disabled:bool, selected:bool }]').isRequired,
10
+ customApps: reactDesc.PropTypes.array.description('Custom items. Format: [{ label:string, icon:component, onClick:func, disabled:bool, selected:bool }]'),
11
+ sectionTitle: reactDesc.PropTypes.string.description('main section title').defaultValue('APPLICATIONS'),
12
+ customSectionTitle: reactDesc.PropTypes.string.description('custom section title').defaultValue('CUSTOM APPLICATIONS'),
13
+ icon: reactDesc.PropTypes.func.description('trigger button s icon').defaultValue(dsIcons.MenuPicker),
14
+ renderTrigger: reactDesc.PropTypes.func.description('Custom trigger component.'),
15
+ actionRef: reactDesc.PropTypes.oneOfType([reactDesc.PropTypes.func, reactDesc.PropTypes.shape({
16
+ current: reactDesc.PropTypes.any
17
+ })]).description('Ref containing a focusToIndex method. This method allows you to focus any App inside the AppPicker.'),
18
+ isOpen: reactDesc.PropTypes.bool.description('Wether the AppPicker should be open or not.'),
19
+ onClose: reactDesc.PropTypes.func.description('Callback function when the AppPicker closes'),
20
+ onKeyDown: reactDesc.PropTypes.func.description('OnKeyDown handler callback.'),
21
+ onClick: reactDesc.PropTypes.func.description('Custom onClick for Trigger component.'),
22
+ onClickOutside: reactDesc.PropTypes.func.description('Callback event when the user clicks outside the App Picker.')
23
+ };
24
+
25
+ exports.propTypes = propTypes;
package/cjs/styles.js ADDED
@@ -0,0 +1,49 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var _taggedTemplateLiteral = require('@babel/runtime/helpers/taggedTemplateLiteral');
6
+ var dsSystem = require('@elliemae/ds-system');
7
+
8
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
9
+
10
+ var _taggedTemplateLiteral__default = /*#__PURE__*/_interopDefaultLegacy(_taggedTemplateLiteral);
11
+
12
+ var _templateObject, _templateObject2, _templateObject3, _templateObject4, _templateObject5, _templateObject6, _templateObject7, _templateObject8;
13
+ const StyledWrapper = dsSystem.styled('ul', {
14
+ name: 'DS-AppPicker',
15
+ slot: 'root'
16
+ })(_templateObject || (_templateObject = _taggedTemplateLiteral__default["default"](["\n display: flex;\n flex-direction: column;\n align-items: center;\n min-width: 244px;\n max-height: 449px;\n overflow-y: auto;\n padding: 4px 0;\n margin: 0 auto;\n\n &:focus {\n outline: none;\n }\n"])));
17
+ const StyledGrid = dsSystem.styled('div', {
18
+ name: 'DS-AppPicker',
19
+ slot: 'grid'
20
+ })(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral__default["default"](["\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-items: center;\n width: 100%;\n padding: 0 8px;\n"])));
21
+ const StyledRow = dsSystem.styled('div', {
22
+ name: 'DS-AppPicker',
23
+ slot: 'row'
24
+ })(_templateObject3 || (_templateObject3 = _taggedTemplateLiteral__default["default"](["\n display: flex;\n width: 100%;\n"])));
25
+ const StyledTitle = dsSystem.styled('p', {
26
+ name: 'DS-AppPicker',
27
+ slot: 'title'
28
+ })(_templateObject4 || (_templateObject4 = _taggedTemplateLiteral__default["default"](["\n color: ", ";\n font-size: 0.8461rem;\n font-weight: bold;\n margin: 8px 0 4px 0;\n line-height: 1;\n text-transform: uppercase;\n"])), props => props.theme.colors.neutral[700]);
29
+ const StyledChipLabel = dsSystem.styled('p', {
30
+ name: 'DS-AppPicker',
31
+ slot: 'chipLabel'
32
+ })(_templateObject5 || (_templateObject5 = _taggedTemplateLiteral__default["default"](["\n font-size: 0.8461rem;\n font-weight: 600;\n word-wrap: break-word;\n margin: 4px 0 0 0;\n line-height: 14px;\n text-overflow: ellipsis;\n max-width: 68px;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n"])));
33
+ const styledChipSelectedCss = dsSystem.css(_templateObject6 || (_templateObject6 = _taggedTemplateLiteral__default["default"](["\n color: ", ";\n background-color: ", ";\n\n .app-picker__icon {\n fill: ", ";\n }\n\n &::after {\n position: absolute;\n content: ' ';\n top: -2px;\n left: -2px;\n width: calc(100% + 4px);\n height: calc(100% + 4px);\n border: 1px solid ", ";\n border-radius: 2px;\n }\n\n &:focus {\n &::after {\n position: absolute;\n content: ' ';\n top: -2px;\n left: -2px;\n width: calc(100% + 4px);\n height: calc(100% + 4px);\n border: 2px solid ", ";\n border-radius: 2px;\n }\n }\n"])), props => props.theme.colors.brand[800], props => props.theme.colors.brand[200], props => props.theme.colors.brand[800], props => props.theme.colors.brand[600], props => props.theme.colors.brand[800]);
34
+ const StyledChip = dsSystem.styled('button', {
35
+ name: 'DS-AppPicker',
36
+ slot: 'chip'
37
+ })(_templateObject7 || (_templateObject7 = _taggedTemplateLiteral__default["default"](["\n display: flex;\n flex-direction: column;\n align-items: center;\n border-radius: 2%;\n padding: 8px 0 4px 0;\n margin: 4px;\n height: 68px;\n width: 68px;\n line-height: 1px;\n background-color: #fff;\n color: ", ";\n cursor: pointer;\n border: 2px solid transparent;\n position: relative;\n\n & .app-picker__icon {\n fill: ", ";\n }\n\n &:hover {\n color: ", ";\n background-color: ", ";\n .app-picker__icon {\n fill: ", ";\n }\n }\n\n &:focus {\n outline: none;\n &::after {\n position: absolute;\n content: ' ';\n top: -2px;\n left: -2px;\n width: calc(100% + 4px);\n height: calc(100% + 4px);\n border: 2px solid ", ";\n border-radius: 2px;\n }\n }\n\n &:disabled {\n color: ", ";\n cursor: not-allowed;\n\n .app-picker__icon {\n fill: ", ";\n }\n\n &:focus,\n &:hover {\n background-color: #fff;\n cursor: not-allowed;\n border: 2px solid transparent;\n\n ", " {\n color: ", ";\n }\n }\n\n &:hover .app-picker__icon {\n fill: ", ";\n }\n }\n\n ", "\n"])), props => props.theme.colors.brand[600], props => props.theme.colors.brand[600], props => props.theme.colors.brand[800], props => props.theme.colors.brand[200], props => props.theme.colors.brand[800], props => props.theme.colors.brand[800], props => props.theme.colors.neutral[500], props => props.theme.colors.neutral[500], StyledChipLabel, props => props.theme.colors.neutral[500], props => props.theme.colors.neutral[500], props => !props.selected ? '' : styledChipSelectedCss);
38
+ const StyledSeparator = dsSystem.styled('hr', {
39
+ name: 'DS-AppPicker',
40
+ slot: 'separator'
41
+ })(_templateObject8 || (_templateObject8 = _taggedTemplateLiteral__default["default"](["\n border-top: 1px solid ", ";\n border-bottom: none;\n width: 99%;\n margin-bottom: 0;\n"])), props => props.theme.colors.neutral[100]);
42
+
43
+ exports.StyledChip = StyledChip;
44
+ exports.StyledChipLabel = StyledChipLabel;
45
+ exports.StyledGrid = StyledGrid;
46
+ exports.StyledRow = StyledRow;
47
+ exports.StyledSeparator = StyledSeparator;
48
+ exports.StyledTitle = StyledTitle;
49
+ exports.StyledWrapper = StyledWrapper;
package/cjs/utils.js CHANGED
@@ -3,14 +3,16 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  const keys = {
6
- LEFT: 37,
7
- UP: 38,
8
- RIGHT: 39,
9
- DOWN: 40,
10
- ENTER: 13,
11
- SPACE: 32,
12
- TAB: 9,
13
- ESC: 27
6
+ LEFT: 'ArrowLeft',
7
+ UP: 'ArrowUp',
8
+ RIGHT: 'ArrowRight',
9
+ DOWN: 'ArrowDown',
10
+ ENTER: 'Enter',
11
+ SPACE: '',
12
+ TAB: 'Tab',
13
+ ESC: 'Escape',
14
+ HOME: 'Home',
15
+ END: 'End'
14
16
  };
15
17
 
16
18
  exports.keys = keys;
@@ -1,155 +1,147 @@
1
1
  import _jsx from '@babel/runtime/helpers/esm/jsx';
2
- import { useRef, useEffect } from 'react';
2
+ import 'core-js/modules/esnext.async-iterator.for-each.js';
3
+ import 'core-js/modules/esnext.iterator.constructor.js';
4
+ import 'core-js/modules/esnext.iterator.for-each.js';
5
+ import 'core-js/modules/esnext.async-iterator.map.js';
6
+ import 'core-js/modules/esnext.iterator.map.js';
7
+ import { useRef, useEffect, useCallback } from 'react';
3
8
  import { chunk } from 'lodash';
4
- import { aggregatedClasses } from '@elliemae/ds-classnames';
5
9
  import { keys } from './utils.js';
6
- import { jsxs, Fragment } from 'react/jsx-runtime';
7
-
8
- var _Separator;
9
- const blockName = 'app-picker';
10
- const Wrapper = aggregatedClasses('div')(blockName, 'wrapper');
11
- const Grid = aggregatedClasses('div')(blockName, 'grid');
12
- const Row = aggregatedClasses('div')(blockName, 'row');
13
- const Chip = aggregatedClasses('div')(blockName, 'chip', ({
14
- disabled
15
- }) => ({
16
- [disabled]: disabled
17
- }));
18
- const ChipLabel = aggregatedClasses('p')(blockName, 'chip-label');
19
- const Separator = aggregatedClasses('hr')(blockName, 'separator');
20
- const SectionTitle = aggregatedClasses('p')(blockName, 'section-title');
21
-
22
- const AppPickerImpl = ({
23
- apps = [],
24
- customApps = [],
25
- sectionTitle = 'APPLICATIONS',
26
- customSectionTitle = 'CUSTOM APPLICATIONS',
27
- close = () => null
28
- }) => {
29
- const wrapperRef = useRef();
10
+ import { StyledWrapper, StyledTitle, StyledGrid, StyledSeparator, StyledRow, StyledChip, StyledChipLabel } from './styles.js';
11
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
12
+
13
+ var _StyledSeparator;
14
+
15
+ const AppPickerImpl = _ref => {
16
+ let {
17
+ apps = [],
18
+ customApps = [],
19
+ sectionTitle = 'APPLICATIONS',
20
+ customSectionTitle = 'CUSTOM APPLICATIONS',
21
+ close = () => null,
22
+ wrapperRef,
23
+ onKeyDown,
24
+ triggerRef
25
+ } = _ref;
26
+ const allFocusableButtons = useRef([]);
27
+ const selectedButton = useRef(null);
30
28
  useEffect(() => {
31
29
  var _wrapperRef$current;
32
30
 
33
- wrapperRef === null || wrapperRef === void 0 ? void 0 : (_wrapperRef$current = wrapperRef.current) === null || _wrapperRef$current === void 0 ? void 0 : _wrapperRef$current.focus();
34
- }, []);
31
+ wrapperRef === null || wrapperRef === void 0 ? void 0 : (_wrapperRef$current = wrapperRef.current) === null || _wrapperRef$current === void 0 ? void 0 : _wrapperRef$current.querySelectorAll('button').forEach((e, index) => {
32
+ if (!e.hasAttribute('disabled')) {
33
+ var _allFocusableButtons$;
35
34
 
36
- const handleKeyDown = ({
37
- target: element,
38
- which: key
39
- }) => {
40
- var _upRow$children$index, _downRow$children$ind;
41
-
42
- const parent = element.parentElement;
43
- const index = [...parent.children].indexOf(element);
44
- const {
45
- previousElementSibling,
46
- nextElementSibling
47
- } = element;
48
- const {
49
- previousElementSibling: upRow,
50
- nextElementSibling: downRow
51
- } = parent;
52
-
53
- switch (key) {
54
- case keys.LEFT:
55
- if (previousElementSibling) {
56
- previousElementSibling.focus();
57
- } else if (upRow) {
58
- upRow.lastChild.focus();
59
- }
35
+ allFocusableButtons === null || allFocusableButtons === void 0 ? void 0 : (_allFocusableButtons$ = allFocusableButtons.current) === null || _allFocusableButtons$ === void 0 ? void 0 : _allFocusableButtons$.push(e);
36
+ }
60
37
 
61
- break;
62
-
63
- case keys.RIGHT:
64
- if (nextElementSibling) {
65
- nextElementSibling.focus();
66
- } else if (downRow) {
67
- downRow.firstChild.focus();
68
- }
38
+ if (e.getAttribute('aria-selected') === 'true') {
39
+ selectedButton.current = index;
40
+ }
41
+ });
69
42
 
70
- break;
43
+ if (selectedButton.current) {
44
+ var _wrapperRef$current2;
71
45
 
72
- case keys.UP:
73
- upRow === null || upRow === void 0 ? void 0 : (_upRow$children$index = upRow.children[index]) === null || _upRow$children$index === void 0 ? void 0 : _upRow$children$index.focus();
74
- break;
46
+ wrapperRef === null || wrapperRef === void 0 ? void 0 : (_wrapperRef$current2 = wrapperRef.current) === null || _wrapperRef$current2 === void 0 ? void 0 : _wrapperRef$current2.querySelectorAll('button')[selectedButton.current].focus();
47
+ } else {
48
+ var _allFocusableButtons$2;
75
49
 
76
- case keys.DOWN:
77
- downRow === null || downRow === void 0 ? void 0 : (_downRow$children$ind = downRow.children[index]) === null || _downRow$children$ind === void 0 ? void 0 : _downRow$children$ind.focus();
78
- break;
50
+ allFocusableButtons === null || allFocusableButtons === void 0 ? void 0 : (_allFocusableButtons$2 = allFocusableButtons.current[0]) === null || _allFocusableButtons$2 === void 0 ? void 0 : _allFocusableButtons$2.focus();
51
+ }
52
+ }, [wrapperRef]); // eslint-disable-next-line max-statements
79
53
 
80
- case keys.ENTER:
81
- case keys.SPACE:
82
- element.click();
83
- break;
54
+ const handleKeyDown = e => {
55
+ var _triggerRef$current;
84
56
 
57
+ switch (e.key) {
85
58
  case keys.ESC:
59
+ triggerRef === null || triggerRef === void 0 ? void 0 : (_triggerRef$current = triggerRef.current) === null || _triggerRef$current === void 0 ? void 0 : _triggerRef$current.focus();
86
60
  close();
87
61
  break;
88
- }
89
- };
90
62
 
91
- const handleOnClick = (e, app) => {
92
- if (!app.disabled) {
93
- close();
94
- app.onClick(e);
63
+ case keys.TAB:
64
+ if (e.shiftKey) {
65
+ if (e.target === allFocusableButtons.current[0]) {
66
+ var _allFocusableButtons$3;
67
+
68
+ e.preventDefault();
69
+ allFocusableButtons === null || allFocusableButtons === void 0 ? void 0 : (_allFocusableButtons$3 = allFocusableButtons.current[allFocusableButtons.current.length - 1]) === null || _allFocusableButtons$3 === void 0 ? void 0 : _allFocusableButtons$3.focus();
70
+ }
71
+ } else if (e.target === allFocusableButtons.current[allFocusableButtons.current.length - 1]) {
72
+ var _allFocusableButtons$4;
73
+
74
+ e.preventDefault();
75
+ allFocusableButtons === null || allFocusableButtons === void 0 ? void 0 : (_allFocusableButtons$4 = allFocusableButtons.current[0]) === null || _allFocusableButtons$4 === void 0 ? void 0 : _allFocusableButtons$4.focus();
76
+ }
77
+
78
+ break;
95
79
  }
96
80
  };
97
81
 
98
- const handleOnHover = ({
99
- target
100
- }) => target.focus();
82
+ const handleOnClick = useCallback((e, app) => {
83
+ if (app.onClick) app.onClick(e, app);
84
+ }, []);
85
+ const handleOnKeyDownWrapper = useCallback(e => {
86
+ if (onKeyDown) onKeyDown(e);
101
87
 
102
- const handleOnKeyDownWrapper = ({
103
- key
104
- }) => {
105
- if (key === keys.ESC) {
88
+ if (!onKeyDown && e.key === keys.ESC) {
106
89
  close();
107
90
  }
108
- };
91
+ }, [onKeyDown, close]);
109
92
 
110
- const buildRows = appList => {
93
+ const buildRows = function (appList) {
94
+ let prevIndex = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
111
95
  const rows = chunk(appList, 3); // divides array in subarrays of 3 items
112
96
 
113
- const formattedRows = rows.map((row, index) => /*#__PURE__*/_jsx(Row, {}, index, row.map((app, key) => {
97
+ const formattedRows = rows.map((row, index) => /*#__PURE__*/_jsx(StyledRow, {}, index, row.map((app, key) => {
114
98
  const {
115
99
  label,
116
100
  disabled,
101
+ selected,
117
102
  icon: Icon
118
103
  } = app;
119
- return /*#__PURE__*/_jsx(Chip, {
120
- classProps: {
121
- disabled
122
- },
104
+ return /*#__PURE__*/_jsx(StyledChip, {
123
105
  onClick: e => handleOnClick(e, app),
124
106
  onKeyDown: handleKeyDown,
125
- onMouseEnter: handleOnHover,
126
- tabIndex: 0,
127
- "data-testid": "app-picker__chip"
107
+ "data-testid": "app-picker__chip",
108
+ "aria-disabled": disabled,
109
+ disabled: disabled,
110
+ selected: selected,
111
+ "aria-selected": selected,
112
+ "aria-setsize": apps.length + customApps.length,
113
+ "aria-posinset": key + prevIndex
128
114
  }, key, /*#__PURE__*/_jsx(Icon, {
129
115
  className: "app-picker__icon",
130
116
  size: "m"
131
- }), /*#__PURE__*/_jsx(ChipLabel, {}, void 0, label));
117
+ }), /*#__PURE__*/_jsx(StyledChipLabel, {}, void 0, label));
132
118
  })));
133
- return formattedRows;
119
+ return /*#__PURE__*/jsx(Fragment, {
120
+ children: formattedRows
121
+ });
134
122
  };
135
123
 
136
- const AppsRows = () => buildRows(apps);
124
+ const AppsRows = () => buildRows(apps, 1);
137
125
 
138
- const CustomRows = () => buildRows(customApps);
126
+ const CustomRows = () => buildRows(customApps, apps.length);
139
127
 
140
- return /*#__PURE__*/jsxs(Wrapper, {
128
+ return /*#__PURE__*/jsxs(StyledWrapper, {
129
+ role: "listbox",
141
130
  ref: wrapperRef,
142
- tabIndex: 0,
143
131
  onKeyDown: handleOnKeyDownWrapper,
144
132
  "data-testid": "app-picker__wrapper",
145
- children: [/*#__PURE__*/_jsx(SectionTitle, {
146
- "data-testid": "app-picker__main-title"
147
- }, void 0, sectionTitle), /*#__PURE__*/_jsx(Grid, {
133
+ children: [/*#__PURE__*/_jsx(StyledTitle, {
134
+ "data-testid": "app-picker__main-title",
135
+ "aria-hidden": true
136
+ }, void 0, sectionTitle), /*#__PURE__*/_jsx(StyledGrid, {
148
137
  "data-testid": "app-picker__main-grid"
149
138
  }, void 0, /*#__PURE__*/_jsx(AppsRows, {})), !!customApps.length && /*#__PURE__*/jsxs(Fragment, {
150
- children: [_Separator || (_Separator = /*#__PURE__*/_jsx(Separator, {})), /*#__PURE__*/_jsx(SectionTitle, {
151
- "data-testid": "app-picker__custom-title"
152
- }, void 0, customSectionTitle), /*#__PURE__*/_jsx(Grid, {
139
+ children: [_StyledSeparator || (_StyledSeparator = /*#__PURE__*/_jsx(StyledSeparator, {
140
+ "aria-hidden": true
141
+ })), /*#__PURE__*/_jsx(StyledTitle, {
142
+ "data-testid": "app-picker__custom-title",
143
+ "aria-hidden": true
144
+ }, void 0, customSectionTitle), /*#__PURE__*/_jsx(StyledGrid, {
153
145
  "data-testid": "app-picker__custom-grid"
154
146
  }, void 0, /*#__PURE__*/_jsx(CustomRows, {}))]
155
147
  })]
@@ -1,57 +1,98 @@
1
1
  import _jsx from '@babel/runtime/helpers/esm/jsx';
2
- import { useState, useEffect } from 'react';
3
- import { PropTypes, describe } from 'react-desc';
4
- import MenuPicker from '@elliemae/ds-icons/MenuPicker';
2
+ import 'core-js/modules/web.dom-collections.iterator.js';
3
+ import { useState, useRef, useEffect } from 'react';
4
+ import { describe } from 'react-desc';
5
+ import { MenuPicker } from '@elliemae/ds-icons';
5
6
  import DSButton from '@elliemae/ds-button';
6
7
  import DSPopover from '@elliemae/ds-popover';
8
+ import { mergeRefs } from '@elliemae/ds-utilities';
7
9
  import AppPickerImpl from './AppPickerImpl.js';
10
+ import { propTypes } from './propTypes.js';
8
11
 
9
- const DSAppPicker = ({
10
- apps = [],
11
- customApps = [],
12
- sectionTitle = 'APPLICATIONS',
13
- customSectionTitle = 'CUSTOM APPLICATIONS',
14
- icon: Icon = () => /*#__PURE__*/_jsx(MenuPicker, {
15
- fill: ['brand-primary', 700],
16
- size: "m"
17
- }),
18
- renderTrigger,
19
- isOpen = false,
20
- onClose = () => null
21
- }) => {
22
- var _AppPickerImpl, _Icon;
12
+ const DSAppPicker = _ref => {
13
+ var _Icon;
23
14
 
24
- const [open, setOpen] = useState(isOpen);
25
- useEffect(() => setOpen(isOpen), [isOpen]);
15
+ let {
16
+ apps = [],
17
+ customApps = [],
18
+ sectionTitle = 'APPLICATIONS',
19
+ customSectionTitle = 'CUSTOM APPLICATIONS',
20
+ icon: Icon = () => /*#__PURE__*/_jsx(MenuPicker, {
21
+ fill: ['brand-primary', 700],
22
+ size: "m"
23
+ }),
24
+ renderTrigger,
25
+ isOpen,
26
+ onClose = () => null,
27
+ actionRef,
28
+ onKeyDown,
29
+ onClick = () => null,
30
+ onClickOutside = () => null,
31
+ triggerRef
32
+ } = _ref;
33
+ const [open, setOpen] = useState(false);
34
+ const wrapperRef = useRef(null);
35
+ const defaultTriggerRef = useRef(null);
36
+ useEffect(() => {
37
+ if (actionRef && actionRef.current) {
38
+ actionRef.current.focusToIndex = index => {
39
+ if (wrapperRef.current) {
40
+ const parent = wrapperRef.current;
41
+ const buttons = [...parent.querySelectorAll('button')];
42
+ buttons[index].focus();
43
+ }
44
+ };
45
+ }
46
+ }, [actionRef, apps, customApps]);
26
47
 
27
48
  const handleOnClose = () => {
49
+ if (typeof isOpen === 'boolean') {
50
+ setOpen(isOpen);
51
+ } else {
52
+ setOpen(false);
53
+ }
54
+
55
+ onClose();
56
+ };
57
+
58
+ const handleOnClickOutside = e => {
28
59
  setOpen(false);
29
60
  onClose();
61
+ onClickOutside(e);
30
62
  };
31
63
 
32
- const AppPickerContent = () => _AppPickerImpl || (_AppPickerImpl = /*#__PURE__*/_jsx(AppPickerImpl, {
64
+ const AppPickerContent = () => /*#__PURE__*/_jsx(AppPickerImpl, {
33
65
  apps: apps,
34
66
  customApps: customApps,
35
67
  sectionTitle: sectionTitle,
36
68
  customSectionTitle: customSectionTitle,
37
- close: handleOnClose
38
- }));
69
+ close: handleOnClose,
70
+ wrapperRef: wrapperRef,
71
+ onKeyDown: onKeyDown,
72
+ triggerRef: triggerRef || defaultTriggerRef
73
+ });
39
74
 
40
- const RenderTrigger = renderTrigger || (({
41
- ref
42
- }) => /*#__PURE__*/_jsx(DSButton, {
43
- "data-testid": "app-picker__button",
44
- id: "app-picker__button",
45
- buttonType: "text",
46
- icon: _Icon || (_Icon = /*#__PURE__*/_jsx(Icon, {})),
47
- innerRef: ref,
48
- onClick: () => setOpen(true)
49
- }));
75
+ const RenderTrigger = renderTrigger || (_ref2 => {
76
+ let {
77
+ ref
78
+ } = _ref2;
79
+ return /*#__PURE__*/_jsx(DSButton, {
80
+ "data-testid": "app-picker__button",
81
+ id: "app-picker__button",
82
+ buttonType: "text",
83
+ icon: _Icon || (_Icon = /*#__PURE__*/_jsx(Icon, {})),
84
+ innerRef: mergeRefs(ref, defaultTriggerRef),
85
+ onClick: e => {
86
+ onClick(e);
87
+ setOpen(true);
88
+ }
89
+ });
90
+ });
50
91
 
51
92
  return /*#__PURE__*/_jsx(DSPopover, {
52
93
  content: /*#__PURE__*/_jsx(AppPickerContent, {}),
53
- isOpen: open,
54
- onClickOutside: handleOnClose,
94
+ isOpen: typeof isOpen === 'boolean' ? isOpen : open,
95
+ onClickOutside: handleOnClickOutside,
55
96
  placement: "bottom",
56
97
  interactionType: "click",
57
98
  renderTrigger: RenderTrigger,
@@ -63,14 +104,7 @@ const DSAppPicker = ({
63
104
  });
64
105
  };
65
106
 
66
- const appPickerProps = {
67
- apps: PropTypes.array.description('Main items. Format: [{ label:string, icon:component, onClick:func, disabled:bool }]').isRequired,
68
- customApps: PropTypes.array.description('Custom items. Format: [{ label:string, icon:component, onClick:func, disabled:bool }]'),
69
- sectionTitle: PropTypes.string.description('main section title').defaultValue('APPLICATIONS'),
70
- customSectionTitle: PropTypes.string.description('custom section title').defaultValue('CUSTOM APPLICATIONS'),
71
- icon: PropTypes.func.description('trigger button s icon').defaultValue(MenuPicker)
72
- };
73
107
  const AppPickerWithSchema = describe(DSAppPicker);
74
- AppPickerWithSchema.propTypes = appPickerProps;
108
+ AppPickerWithSchema.propTypes = propTypes;
75
109
 
76
110
  export { AppPickerWithSchema, DSAppPicker, DSAppPicker as default };
@@ -0,0 +1,21 @@
1
+ import { PropTypes } from 'react-desc';
2
+ import { MenuPicker } from '@elliemae/ds-icons';
3
+
4
+ const propTypes = {
5
+ apps: PropTypes.array.description('Main items. Format: [{ label:string, icon:component, onClick:func, disabled:bool, selected:bool }]').isRequired,
6
+ customApps: PropTypes.array.description('Custom items. Format: [{ label:string, icon:component, onClick:func, disabled:bool, selected:bool }]'),
7
+ sectionTitle: PropTypes.string.description('main section title').defaultValue('APPLICATIONS'),
8
+ customSectionTitle: PropTypes.string.description('custom section title').defaultValue('CUSTOM APPLICATIONS'),
9
+ icon: PropTypes.func.description('trigger button s icon').defaultValue(MenuPicker),
10
+ renderTrigger: PropTypes.func.description('Custom trigger component.'),
11
+ actionRef: PropTypes.oneOfType([PropTypes.func, PropTypes.shape({
12
+ current: PropTypes.any
13
+ })]).description('Ref containing a focusToIndex method. This method allows you to focus any App inside the AppPicker.'),
14
+ isOpen: PropTypes.bool.description('Wether the AppPicker should be open or not.'),
15
+ onClose: PropTypes.func.description('Callback function when the AppPicker closes'),
16
+ onKeyDown: PropTypes.func.description('OnKeyDown handler callback.'),
17
+ onClick: PropTypes.func.description('Custom onClick for Trigger component.'),
18
+ onClickOutside: PropTypes.func.description('Callback event when the user clicks outside the App Picker.')
19
+ };
20
+
21
+ export { propTypes };
package/esm/styles.js ADDED
@@ -0,0 +1,35 @@
1
+ import _taggedTemplateLiteral from '@babel/runtime/helpers/esm/taggedTemplateLiteral';
2
+ import { styled, css } from '@elliemae/ds-system';
3
+
4
+ var _templateObject, _templateObject2, _templateObject3, _templateObject4, _templateObject5, _templateObject6, _templateObject7, _templateObject8;
5
+ const StyledWrapper = styled('ul', {
6
+ name: 'DS-AppPicker',
7
+ slot: 'root'
8
+ })(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n display: flex;\n flex-direction: column;\n align-items: center;\n min-width: 244px;\n max-height: 449px;\n overflow-y: auto;\n padding: 4px 0;\n margin: 0 auto;\n\n &:focus {\n outline: none;\n }\n"])));
9
+ const StyledGrid = styled('div', {
10
+ name: 'DS-AppPicker',
11
+ slot: 'grid'
12
+ })(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-items: center;\n width: 100%;\n padding: 0 8px;\n"])));
13
+ const StyledRow = styled('div', {
14
+ name: 'DS-AppPicker',
15
+ slot: 'row'
16
+ })(_templateObject3 || (_templateObject3 = _taggedTemplateLiteral(["\n display: flex;\n width: 100%;\n"])));
17
+ const StyledTitle = styled('p', {
18
+ name: 'DS-AppPicker',
19
+ slot: 'title'
20
+ })(_templateObject4 || (_templateObject4 = _taggedTemplateLiteral(["\n color: ", ";\n font-size: 0.8461rem;\n font-weight: bold;\n margin: 8px 0 4px 0;\n line-height: 1;\n text-transform: uppercase;\n"])), props => props.theme.colors.neutral[700]);
21
+ const StyledChipLabel = styled('p', {
22
+ name: 'DS-AppPicker',
23
+ slot: 'chipLabel'
24
+ })(_templateObject5 || (_templateObject5 = _taggedTemplateLiteral(["\n font-size: 0.8461rem;\n font-weight: 600;\n word-wrap: break-word;\n margin: 4px 0 0 0;\n line-height: 14px;\n text-overflow: ellipsis;\n max-width: 68px;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n"])));
25
+ const styledChipSelectedCss = css(_templateObject6 || (_templateObject6 = _taggedTemplateLiteral(["\n color: ", ";\n background-color: ", ";\n\n .app-picker__icon {\n fill: ", ";\n }\n\n &::after {\n position: absolute;\n content: ' ';\n top: -2px;\n left: -2px;\n width: calc(100% + 4px);\n height: calc(100% + 4px);\n border: 1px solid ", ";\n border-radius: 2px;\n }\n\n &:focus {\n &::after {\n position: absolute;\n content: ' ';\n top: -2px;\n left: -2px;\n width: calc(100% + 4px);\n height: calc(100% + 4px);\n border: 2px solid ", ";\n border-radius: 2px;\n }\n }\n"])), props => props.theme.colors.brand[800], props => props.theme.colors.brand[200], props => props.theme.colors.brand[800], props => props.theme.colors.brand[600], props => props.theme.colors.brand[800]);
26
+ const StyledChip = styled('button', {
27
+ name: 'DS-AppPicker',
28
+ slot: 'chip'
29
+ })(_templateObject7 || (_templateObject7 = _taggedTemplateLiteral(["\n display: flex;\n flex-direction: column;\n align-items: center;\n border-radius: 2%;\n padding: 8px 0 4px 0;\n margin: 4px;\n height: 68px;\n width: 68px;\n line-height: 1px;\n background-color: #fff;\n color: ", ";\n cursor: pointer;\n border: 2px solid transparent;\n position: relative;\n\n & .app-picker__icon {\n fill: ", ";\n }\n\n &:hover {\n color: ", ";\n background-color: ", ";\n .app-picker__icon {\n fill: ", ";\n }\n }\n\n &:focus {\n outline: none;\n &::after {\n position: absolute;\n content: ' ';\n top: -2px;\n left: -2px;\n width: calc(100% + 4px);\n height: calc(100% + 4px);\n border: 2px solid ", ";\n border-radius: 2px;\n }\n }\n\n &:disabled {\n color: ", ";\n cursor: not-allowed;\n\n .app-picker__icon {\n fill: ", ";\n }\n\n &:focus,\n &:hover {\n background-color: #fff;\n cursor: not-allowed;\n border: 2px solid transparent;\n\n ", " {\n color: ", ";\n }\n }\n\n &:hover .app-picker__icon {\n fill: ", ";\n }\n }\n\n ", "\n"])), props => props.theme.colors.brand[600], props => props.theme.colors.brand[600], props => props.theme.colors.brand[800], props => props.theme.colors.brand[200], props => props.theme.colors.brand[800], props => props.theme.colors.brand[800], props => props.theme.colors.neutral[500], props => props.theme.colors.neutral[500], StyledChipLabel, props => props.theme.colors.neutral[500], props => props.theme.colors.neutral[500], props => !props.selected ? '' : styledChipSelectedCss);
30
+ const StyledSeparator = styled('hr', {
31
+ name: 'DS-AppPicker',
32
+ slot: 'separator'
33
+ })(_templateObject8 || (_templateObject8 = _taggedTemplateLiteral(["\n border-top: 1px solid ", ";\n border-bottom: none;\n width: 99%;\n margin-bottom: 0;\n"])), props => props.theme.colors.neutral[100]);
34
+
35
+ export { StyledChip, StyledChipLabel, StyledGrid, StyledRow, StyledSeparator, StyledTitle, StyledWrapper };
package/esm/utils.js CHANGED
@@ -1,12 +1,14 @@
1
1
  const keys = {
2
- LEFT: 37,
3
- UP: 38,
4
- RIGHT: 39,
5
- DOWN: 40,
6
- ENTER: 13,
7
- SPACE: 32,
8
- TAB: 9,
9
- ESC: 27
2
+ LEFT: 'ArrowLeft',
3
+ UP: 'ArrowUp',
4
+ RIGHT: 'ArrowRight',
5
+ DOWN: 'ArrowDown',
6
+ ENTER: 'Enter',
7
+ SPACE: '',
8
+ TAB: 'Tab',
9
+ ESC: 'Escape',
10
+ HOME: 'Home',
11
+ END: 'End'
10
12
  };
11
13
 
12
14
  export { keys };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elliemae/ds-app-picker",
3
- "version": "2.0.0-alpha.1",
3
+ "version": "2.0.0-alpha.13",
4
4
  "license": "MIT",
5
5
  "description": "ICE MT - Dimsum - App Picker",
6
6
  "module": "./esm/index.js",
@@ -19,6 +19,14 @@
19
19
  "import": "./esm/types/AppPickerTypes.js",
20
20
  "require": "./cjs/types/AppPickerTypes.js"
21
21
  },
22
+ "./styles": {
23
+ "import": "./esm/styles.js",
24
+ "require": "./cjs/styles.js"
25
+ },
26
+ "./propTypes": {
27
+ "import": "./esm/propTypes.js",
28
+ "require": "./cjs/propTypes.js"
29
+ },
22
30
  "./DSAppPicker": {
23
31
  "import": "./esm/DSAppPicker.js",
24
32
  "require": "./cjs/DSAppPicker.js"
@@ -48,16 +56,23 @@
48
56
  "build": "node ../../scripts/build/build.js"
49
57
  },
50
58
  "dependencies": {
51
- "@elliemae/ds-button": "2.0.0-alpha.1",
52
- "@elliemae/ds-classnames": "2.0.0-alpha.1",
53
- "@elliemae/ds-icons": "2.0.0-alpha.1",
54
- "@elliemae/ds-popover": "2.0.0-alpha.1",
59
+ "@elliemae/ds-button": "2.0.0-alpha.13",
60
+ "@elliemae/ds-icons": "2.0.0-alpha.13",
61
+ "@elliemae/ds-popover": "2.0.0-alpha.13",
62
+ "@elliemae/ds-system": "2.0.0-alpha.13",
63
+ "@elliemae/ds-utilities": "2.0.0-alpha.13",
55
64
  "react-desc": "^4.1.3"
56
65
  },
66
+ "devDependencies": {
67
+ "@testing-library/jest-dom": "~5.15.0",
68
+ "@testing-library/react": "~12.1.2",
69
+ "styled-components": "~5.3.3"
70
+ },
57
71
  "peerDependencies": {
58
72
  "lodash": "^4.17.21",
59
73
  "react": "^17.0.2",
60
- "react-dom": "^17.0.2"
74
+ "react-dom": "^17.0.2",
75
+ "styled-components": "^5.3.3"
61
76
  },
62
77
  "publishConfig": {
63
78
  "access": "public",
@@ -1,5 +1,11 @@
1
+ /// <reference path="../../../../shared/typings/react-desc.d.ts" />
2
+ import React from 'react';
1
3
  import type { DSAppPickerType } from './types/AppPickerTypes';
2
4
  declare const DSAppPicker: DSAppPickerType;
3
- declare const AppPickerWithSchema: any;
5
+ declare const AppPickerWithSchema: {
6
+ (props?: React.PropsWithChildren<import("./types/AppPickerTypes").AppPickerPropsType> | undefined): JSX.Element;
7
+ propTypes: unknown;
8
+ toTypescript: () => import("react-desc").TypescriptSchema;
9
+ };
4
10
  export { DSAppPicker, AppPickerWithSchema };
5
11
  export default DSAppPicker;
@@ -0,0 +1,61 @@
1
+ /// <reference path="../../../../shared/typings/react-desc.d.ts" />
2
+ export declare const propTypes: {
3
+ apps: import("react-desc").PropTypesDescValidator;
4
+ customApps: {
5
+ defaultValue<T = unknown>(arg: T): {
6
+ deprecated: import("react-desc").PropTypesDescValidator;
7
+ };
8
+ isRequired: import("react-desc").PropTypesDescValidator;
9
+ };
10
+ sectionTitle: {
11
+ deprecated: import("react-desc").PropTypesDescValidator;
12
+ };
13
+ customSectionTitle: {
14
+ deprecated: import("react-desc").PropTypesDescValidator;
15
+ };
16
+ icon: {
17
+ deprecated: import("react-desc").PropTypesDescValidator;
18
+ };
19
+ renderTrigger: {
20
+ defaultValue<T = unknown>(arg: T): {
21
+ deprecated: import("react-desc").PropTypesDescValidator;
22
+ };
23
+ isRequired: import("react-desc").PropTypesDescValidator;
24
+ };
25
+ actionRef: {
26
+ defaultValue<T = unknown>(arg: T): {
27
+ deprecated: import("react-desc").PropTypesDescValidator;
28
+ };
29
+ isRequired: import("react-desc").PropTypesDescValidator;
30
+ };
31
+ isOpen: {
32
+ defaultValue<T = unknown>(arg: T): {
33
+ deprecated: import("react-desc").PropTypesDescValidator;
34
+ };
35
+ isRequired: import("react-desc").PropTypesDescValidator;
36
+ };
37
+ onClose: {
38
+ defaultValue<T = unknown>(arg: T): {
39
+ deprecated: import("react-desc").PropTypesDescValidator;
40
+ };
41
+ isRequired: import("react-desc").PropTypesDescValidator;
42
+ };
43
+ onKeyDown: {
44
+ defaultValue<T = unknown>(arg: T): {
45
+ deprecated: import("react-desc").PropTypesDescValidator;
46
+ };
47
+ isRequired: import("react-desc").PropTypesDescValidator;
48
+ };
49
+ onClick: {
50
+ defaultValue<T = unknown>(arg: T): {
51
+ deprecated: import("react-desc").PropTypesDescValidator;
52
+ };
53
+ isRequired: import("react-desc").PropTypesDescValidator;
54
+ };
55
+ onClickOutside: {
56
+ defaultValue<T = unknown>(arg: T): {
57
+ deprecated: import("react-desc").PropTypesDescValidator;
58
+ };
59
+ isRequired: import("react-desc").PropTypesDescValidator;
60
+ };
61
+ };
@@ -0,0 +1,9 @@
1
+ export declare const StyledWrapper: import("styled-components").StyledComponent<"ul", import("@elliemae/ds-system/types/styled/types").Theme, {}, never>;
2
+ export declare const StyledGrid: import("styled-components").StyledComponent<"div", import("@elliemae/ds-system/types/styled/types").Theme, {}, never>;
3
+ export declare const StyledRow: import("styled-components").StyledComponent<"div", import("@elliemae/ds-system/types/styled/types").Theme, {}, never>;
4
+ export declare const StyledTitle: import("styled-components").StyledComponent<"p", import("@elliemae/ds-system/types/styled/types").Theme, {}, never>;
5
+ export declare const StyledChipLabel: import("styled-components").StyledComponent<"p", import("@elliemae/ds-system/types/styled/types").Theme, {}, never>;
6
+ export declare const StyledChip: import("styled-components").StyledComponent<"button", import("@elliemae/ds-system/types/styled/types").Theme, {
7
+ selected: boolean | undefined;
8
+ }, never>;
9
+ export declare const StyledSeparator: import("styled-components").StyledComponent<"hr", import("@elliemae/ds-system/types/styled/types").Theme, {}, never>;
@@ -2,9 +2,13 @@
2
2
  import type { SvgIconType } from '@elliemae/ds-icons';
3
3
  export interface AppItemType {
4
4
  label: string;
5
- icon: React.Component;
6
- onClick: () => {};
7
- disabled: boolean;
5
+ icon: React.ComponentType<{
6
+ className: string;
7
+ size: string;
8
+ }>;
9
+ onClick: () => void | null;
10
+ disabled?: boolean;
11
+ selected?: boolean;
8
12
  }
9
13
  export interface AppPickerPropsType {
10
14
  apps: AppItemType[];
@@ -12,14 +16,26 @@ export interface AppPickerPropsType {
12
16
  sectionTitle: string;
13
17
  customSectionTitle: string;
14
18
  icon: SvgIconType;
19
+ onKeyDown?: (e: React.KeyboardEvent) => void;
20
+ actionRef?: React.RefObject<{
21
+ focusToIndex: (index: number) => void;
22
+ }>;
23
+ onClick?: (e: MouseEvent) => void;
24
+ onClickOutside?: (e: React.MouseEvent) => void;
25
+ renderTrigger?: React.ComponentType<any>;
26
+ isOpen?: boolean;
27
+ onClose?: () => void;
28
+ triggerRef?: React.RefObject<HTMLButtonElement>;
15
29
  }
16
30
  export interface AppPickerImplProps {
17
31
  apps: AppItemType[];
18
32
  customApps: AppItemType[];
19
33
  sectionTitle: string;
20
34
  customSectionTitle: string;
21
- close: () => void;
22
- isOpen: boolean;
35
+ close?: () => void;
36
+ onKeyDown: (e: KeyboardEvent) => void;
37
+ wrapperRef: React.RefObject<HTMLUListElement>;
38
+ triggerRef: React.RefObject<HTMLElement>;
23
39
  }
24
40
  export declare type DSAppPickerImplType = React.ComponentType<AppPickerImplProps>;
25
41
  export declare type DSAppPickerType = React.ComponentType<AppPickerPropsType>;
package/types/utils.d.ts CHANGED
@@ -1,10 +1,12 @@
1
1
  export declare const keys: {
2
- LEFT: number;
3
- UP: number;
4
- RIGHT: number;
5
- DOWN: number;
6
- ENTER: number;
7
- SPACE: number;
8
- TAB: number;
9
- ESC: number;
2
+ LEFT: string;
3
+ UP: string;
4
+ RIGHT: string;
5
+ DOWN: string;
6
+ ENTER: string;
7
+ SPACE: string;
8
+ TAB: string;
9
+ ESC: string;
10
+ HOME: string;
11
+ END: string;
10
12
  };
package/cjs/package.json DELETED
@@ -1,7 +0,0 @@
1
- {
2
- "type": "commonjs",
3
- "sideEffects": [
4
- "*.css",
5
- "*.scss"
6
- ]
7
- }
package/esm/package.json DELETED
@@ -1,7 +0,0 @@
1
- {
2
- "type": "module",
3
- "sideEffects": [
4
- "*.css",
5
- "*.scss"
6
- ]
7
- }