@rh-support/cases 2.1.69 → 2.1.70

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.
@@ -98,6 +98,6 @@ export function AccountsBookmarkedGroupFilter(props) {
98
98
  return null;
99
99
  }
100
100
  return (React.createElement("div", { className: "pf-v5-c-select" },
101
- React.createElement("label", { htmlFor: "case-list-group-filter" }, canAccessManagedAccounts ? t('Accounts') : t(filterNamesMap[SolrKeys.accountNumber])),
102
- React.createElement(Select, { variant: SelectVariant.typeaheadMulti, onToggle: (_event, open) => onBookmarksToggle(open), onSelect: onBookmarksSelect, onClear: onBookmarksClear, isOpen: isOpen, onFilter: onBookmarksFilter, placeholderText: getDropdownBtnPlaceholder(t(canAccessManagedAccounts ? 'Select an account' : 'Search for a bookmark'), canAccessManagedAccounts ? selectedAccounts : selectedBookmarks, '', dropdownOptions.length, canAccessManagedAccounts ? t('All accounts') : t('All bookmarks')), "data-tracking-id": "accounts-filter", id: "case-list-group-filter" }, getBookmarksOptions(dropdownOptions))));
101
+ React.createElement("label", { htmlFor: "case-list-account-filter", className: "case-list-account-filter-label" }, canAccessManagedAccounts ? t('Accounts') : t(filterNamesMap[SolrKeys.accountNumber])),
102
+ React.createElement(Select, { variant: SelectVariant.typeaheadMulti, onToggle: (_event, open) => onBookmarksToggle(open), onSelect: onBookmarksSelect, onClear: onBookmarksClear, isOpen: isOpen, onFilter: onBookmarksFilter, placeholderText: getDropdownBtnPlaceholder(t(canAccessManagedAccounts ? 'Select an account' : 'Search for a bookmark'), canAccessManagedAccounts ? selectedAccounts : selectedBookmarks, '', dropdownOptions.length, canAccessManagedAccounts ? t('All accounts') : t('All bookmarks')), "data-tracking-id": "accounts-filter", id: "case-list-account-filter" }, getBookmarksOptions(dropdownOptions))));
103
103
  }
@@ -209,7 +209,7 @@ export function CreatorSsoNameFilter() {
209
209
  React.createElement(TextInputGroupMain, { value: externalQuery, onClick: onExternalToggle, onKeyDown: onInputKeyDown, placeholder: getDropdownBtnPlaceholder(t('Search for a name'), selectedExternalContacts.map((i) => i.label), ' ', filteredOptions.length + dropdownHeader.length, t('All contacts')), onChange: onExternalQueryChange, isExpanded: isExternalOpen, innerRef: externalInputRef, "aria-controls": "external-ssousername-filter", role: "combobox" }),
210
210
  React.createElement(TextInputGroupUtilities, null, !isEmpty(externalQuery) && (React.createElement(Button, { variant: "plain", onClick: () => setExternalQuery(''), isDisabled: isFetchingExternalContacts, "aria-label": t('Clear') }, isFetchingExternalContacts ? (React.createElement(LoadingIndicator, { show: true, size: "sm" })) : (React.createElement(TimesCircleIcon, { "aria-hidden": true }))))))));
211
211
  return (React.createElement("div", null,
212
- React.createElement("label", { htmlFor: "case-list-username-filter" },
212
+ React.createElement("label", { htmlFor: "case-list-username-filter", className: "case-list-username-filter-label" },
213
213
  React.createElement(Trans, null, filterNamesMap['usernameFilterTitle'])),
214
214
  !loggedInUserRights.data.isInternal() ? (React.createElement(Select, { id: "external-ssousername-filter", "data-tracking-id": "external-ssousername-filter", isOpen: isExternalOpen, onOpenChange: () => setIsExternalOpen(false), toggle: externalToggle, popperProps: { direction: 'down', enableFlip: false }, isScrollable: true, onSelect: (_e, v) => onSelectExternal(v) },
215
215
  React.createElement(SelectList, null, filteredExternalList))) : (React.createElement(InternalSsoNameFilter, null))));
@@ -1 +1 @@
1
- {"version":3,"file":"GroupsFilter.d.ts","sourceRoot":"","sources":["../../../../../src/components/case-list/case-list-filters/GroupsFilter.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAqB,MAAM,OAAO,CAAC;AAmB1C,wBAAgB,YAAY,sBA6D3B"}
1
+ {"version":3,"file":"GroupsFilter.d.ts","sourceRoot":"","sources":["../../../../../src/components/case-list/case-list-filters/GroupsFilter.tsx"],"names":[],"mappings":"AAmBA,OAAO,KAA2D,MAAM,OAAO,CAAC;AAShF,wBAAgB,YAAY,sBA2P3B"}
@@ -1,11 +1,13 @@
1
- import { MultiSelectDropDownList } from '@rh-support/components';
1
+ import { Button, Divider, MenuToggle, Select, SelectList, SelectOption, TextInputGroup, TextInputGroupMain, TextInputGroupUtilities, } from '@patternfly/react-core';
2
+ import TimesIcon from '@patternfly/react-icons/dist/js/icons/times-icon';
2
3
  import { GlobalMetadataStateContext } from '@rh-support/react-context';
3
4
  import { ability, resourceActions, resources } from '@rh-support/user-permissions';
4
- import { getDropdownBtnPlaceholder, toOptions } from '@rh-support/utils';
5
- import React, { useContext } from 'react';
5
+ import { getDropdownBtnPlaceholder } from '@rh-support/utils';
6
+ import isEmpty from 'lodash/isEmpty';
7
+ import isUndefined from 'lodash/isUndefined';
8
+ import React, { useContext, useEffect, useMemo, useRef, useState } from 'react';
6
9
  import { Trans, useTranslation } from 'react-i18next';
7
10
  import { Link } from 'react-router-dom';
8
- import { FILTER_SEARCHABLE_MODE_LIMIT } from '../../../enums/caseSearch';
9
11
  import { SolrKeys } from '../../../enums/filters';
10
12
  import { filterNamesMap } from '../../../utils/constants';
11
13
  import { CaseListFilterDispatchContext, CaseListFilterStateContext } from '../CaseListFilterContext';
@@ -15,30 +17,157 @@ export function GroupsFilter() {
15
17
  const { globalMetadataState: { caseGroups }, } = useContext(GlobalMetadataStateContext);
16
18
  const { filterInfo } = useContext(CaseListFilterStateContext);
17
19
  const dispatch = useContext(CaseListFilterDispatchContext);
20
+ const [inputValue, setInputValue] = useState('');
21
+ const [focusedItemIndex, setFocusedItemIndex] = useState(null);
22
+ const [activeItem, setActiveItem] = useState(null);
23
+ const list = [
24
+ ...(caseGroups.data ? caseGroups === null || caseGroups === void 0 ? void 0 : caseGroups.data.map((group) => ({ value: group.name, key: group.groupNum })) : []),
25
+ ];
26
+ const [selectOptions, setSelectOptions] = useState(list);
18
27
  const canViewManageTab = ability.can(resourceActions.READ, resources.MANAGE);
19
28
  const canViewCaseGroups = ability.can(resourceActions.READ, resources.CASE_GROUPS) && canViewManageTab;
20
- const onGroupFilterChange = (selectedGroups) => {
21
- selectedGroups = selectedGroups.map((i) => i.value);
22
- updateFilter(dispatch, { filterKey: SolrKeys.group, values: selectedGroups });
29
+ const [isOpen, setIsOpen] = useState(false);
30
+ const [selectedItems, setSelectedItems] = useState({});
31
+ const textInputRef = useRef();
32
+ const onToggleClick = () => {
33
+ setIsOpen(!isOpen);
23
34
  };
24
- const isFilterSearchable = caseGroups.data && caseGroups.data.length > FILTER_SEARCHABLE_MODE_LIMIT;
25
- const list = [
26
- ...(caseGroups.data ? caseGroups.data.map((group) => ({ value: group.name, key: group.groupNum })) : []),
27
- {
28
- children: canViewCaseGroups ? (React.createElement(React.Fragment, null,
29
- React.createElement("div", { className: "pf-v5-c-divider", role: "separator" }),
30
- React.createElement(Link, { className: "cta cta-link pf-v5-c-select__menu-item", to: "/manage/groups" },
31
- React.createElement(Trans, null, "Manage groups")))) : (React.createElement(React.Fragment, null)),
32
- isNotSearchable: true,
33
- isNonActionable: true,
34
- isNotSelectable: true,
35
- },
36
- ];
37
- return (React.createElement(MultiSelectDropDownList, { placeholder: getDropdownBtnPlaceholder(t('Select a group'), filterInfo[SolrKeys.group], ' ', (caseGroups.data || []).length, t('All selected')), "data-tracking-id": "groups-filter", id: "case-list-group-filter", title: t(filterNamesMap[SolrKeys.group]), label: t(filterNamesMap[SolrKeys.group]), searchable: isFilterSearchable, onChange: onGroupFilterChange, selectedItems: toOptions(filterInfo[SolrKeys.group], { labelKey: 'value' }), list: toOptions(list, {
38
- labelKey: 'value',
39
- childrenKey: 'children',
40
- nonSearchableItemKey: 'isNotSearchable',
41
- nonActionableItemKey: 'isNonActionable',
42
- nonSelectableItemKey: 'isNotSelectable',
43
- }) }));
35
+ const onSelect = (option) => {
36
+ const prevSelectedGroups = Object.keys(selectedItems).filter((item) => selectedItems[item] && item);
37
+ if (selectedItems[option.value]) {
38
+ setSelectedItems((prevData) => (Object.assign(Object.assign({}, prevData), { [option.value]: !prevData[option.value] })));
39
+ const indexOfGroup = prevSelectedGroups.indexOf(option.value);
40
+ prevSelectedGroups.splice(indexOfGroup, 1);
41
+ }
42
+ else {
43
+ setSelectedItems((prevData) => (Object.assign(Object.assign({}, prevData), { [option.value]: true })));
44
+ prevSelectedGroups.push(option.value);
45
+ }
46
+ const mapValueToKey = (value) => {
47
+ const selectedItem = list.find((item) => item.value === value);
48
+ return selectedItem;
49
+ };
50
+ const updatedFilterValues = prevSelectedGroups.map((item) => mapValueToKey(item));
51
+ updateFilter(dispatch, {
52
+ filterKey: SolrKeys.group,
53
+ values: updatedFilterValues,
54
+ });
55
+ };
56
+ const handleMenuArrowKeys = (key) => {
57
+ let indexToFocus;
58
+ if (isOpen) {
59
+ if (key === 'ArrowUp') {
60
+ // When no index is set or at the first index, focus to the last, otherwise decrement focus index
61
+ if (focusedItemIndex === null || focusedItemIndex === 0) {
62
+ indexToFocus = (list === null || list === void 0 ? void 0 : list.length) - 1;
63
+ }
64
+ else {
65
+ indexToFocus = focusedItemIndex - 1;
66
+ }
67
+ }
68
+ if (key === 'ArrowDown') {
69
+ // When no index is set or at the last index, focus to the first, otherwise increment focus index
70
+ if (focusedItemIndex === null || focusedItemIndex === (list === null || list === void 0 ? void 0 : list.length) - 1) {
71
+ indexToFocus = 0;
72
+ }
73
+ else {
74
+ indexToFocus = focusedItemIndex + 1;
75
+ }
76
+ }
77
+ setFocusedItemIndex(indexToFocus);
78
+ const focusedItem = list[indexToFocus];
79
+ setActiveItem(`select-multi-typeahead-checkbox-${focusedItem === null || focusedItem === void 0 ? void 0 : focusedItem.value.replace(' ', '-')}`);
80
+ }
81
+ };
82
+ const onInputKeyDown = (event) => {
83
+ const focusedItem = focusedItemIndex ? list[focusedItemIndex] : list[0];
84
+ switch (event.key) {
85
+ // Select the first available option
86
+ case 'Enter':
87
+ if (!isOpen) {
88
+ setIsOpen((prevIsOpen) => !prevIsOpen);
89
+ }
90
+ else if (isOpen && (focusedItem === null || focusedItem === void 0 ? void 0 : focusedItem.value) !== 'no results') {
91
+ onSelect(focusedItem === null || focusedItem === void 0 ? void 0 : focusedItem.value);
92
+ }
93
+ break;
94
+ case 'Tab':
95
+ case 'Escape':
96
+ setIsOpen(false);
97
+ setActiveItem(null);
98
+ break;
99
+ case 'ArrowUp':
100
+ case 'ArrowDown':
101
+ event.preventDefault();
102
+ handleMenuArrowKeys(event.key);
103
+ break;
104
+ }
105
+ };
106
+ const onTextInputChange = (_event, value) => {
107
+ setInputValue(value);
108
+ };
109
+ const placeholder = getDropdownBtnPlaceholder(t('Select a group'), filterInfo[SolrKeys.group], '', (caseGroups.data || []).length, t('All selected'));
110
+ const onClick = () => {
111
+ var _a;
112
+ setInputValue('');
113
+ setSelectedItems({});
114
+ updateFilter(dispatch, {
115
+ filterKey: SolrKeys.group,
116
+ values: [],
117
+ });
118
+ (_a = textInputRef === null || textInputRef === void 0 ? void 0 : textInputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
119
+ };
120
+ const toggle = (toggleRef) => (React.createElement(MenuToggle, { variant: "typeahead", onClick: onToggleClick, innerRef: toggleRef, isExpanded: isOpen, isFullWidth: true },
121
+ React.createElement(TextInputGroup, { isPlain: true },
122
+ React.createElement(TextInputGroupMain, Object.assign({ value: inputValue, onClick: onToggleClick, onChange: onTextInputChange, onKeyDown: onInputKeyDown, id: "multi-typeahead-select-checkbox-input", autoComplete: "off", innerRef: textInputRef, placeholder: placeholder }, (activeItem && { 'aria-activedescendant': activeItem }), { role: "combobox", isExpanded: isOpen, "aria-controls": "select-multi-typeahead-checkbox-listbox" })),
123
+ Object.keys(selectedItems).filter((key) => selectedItems[key] === true).length ? (React.createElement(TextInputGroupUtilities, null,
124
+ React.createElement(Button, { variant: "plain", onClick: onClick, "aria-label": "Clear input value" },
125
+ React.createElement(TimesIcon, { "aria-hidden": true })))) : (''))));
126
+ const dropdownOptions = useMemo(() => {
127
+ return (!isEmpty(selectOptions) ? selectOptions : list).map((option, index) => !isUndefined(option.value) && (React.createElement(SelectOption, { key: option.key, value: option, hasCheckbox: true, isSelected: selectedItems[option.value], isFocused: focusedItemIndex !== null && focusedItemIndex === index, id: `select-multi-typeahead-${option.value.replace(' ', '-')}` }, option.value)));
128
+ // eslint-disable-next-line react-hooks/exhaustive-deps
129
+ }, [selectedItems, caseGroups === null || caseGroups === void 0 ? void 0 : caseGroups.data, selectOptions, focusedItemIndex]);
130
+ useEffect(() => {
131
+ let newSelectOptions = list;
132
+ // Filter menu items based on the text input value when one exists
133
+ if (inputValue) {
134
+ newSelectOptions = list.filter((option) => String(option === null || option === void 0 ? void 0 : option.value).toLowerCase().startsWith(inputValue.toLowerCase()));
135
+ // When no options are found after filtering, display 'No results found'
136
+ if (!newSelectOptions.length) {
137
+ newSelectOptions = [{ isDisabled: false, children: `No results found` }];
138
+ }
139
+ // Open the menu when the input value changes and the new value is not empty
140
+ if (!isOpen) {
141
+ setIsOpen(true);
142
+ }
143
+ }
144
+ setSelectOptions(newSelectOptions);
145
+ setFocusedItemIndex(null);
146
+ setActiveItem(null);
147
+ // eslint-disable-next-line react-hooks/exhaustive-deps
148
+ }, [inputValue]);
149
+ useEffect(() => {
150
+ var _a;
151
+ if (isEmpty(filterInfo[SolrKeys.group])) {
152
+ setSelectedItems({});
153
+ }
154
+ else {
155
+ const selectedGroups = (_a = filterInfo[SolrKeys.group]) === null || _a === void 0 ? void 0 : _a.reduce((selection, group) => {
156
+ selection[group === null || group === void 0 ? void 0 : group.value] = true;
157
+ return selection;
158
+ }, {});
159
+ setSelectedItems(selectedGroups);
160
+ }
161
+ // eslint-disable-next-line react-hooks/exhaustive-deps
162
+ }, [filterInfo[SolrKeys.group]]);
163
+ return (React.createElement("div", { className: "group-filter" },
164
+ React.createElement("label", { htmlFor: "case-list-group-filter", className: "pf-v5-u-mb-sm group-filter-label" }, t(filterNamesMap[SolrKeys.group])),
165
+ React.createElement("div", { className: "pf-v5-c-select" },
166
+ React.createElement(Select, { role: "menu", "data-tracking-id": "groups-filter", id: "case-list-group-filter", title: t(filterNamesMap[SolrKeys.group]), isOpen: isOpen, selected: selectedItems, onSelect: (_e, v) => onSelect(v), onOpenChange: (nextOpen) => setIsOpen(nextOpen), popperProps: { direction: 'down', enableFlip: false }, toggle: toggle, isScrollable: true },
167
+ React.createElement(SelectList, null, dropdownOptions),
168
+ canViewCaseGroups && (React.createElement(React.Fragment, null,
169
+ React.createElement(Divider, null),
170
+ React.createElement(SelectList, null,
171
+ React.createElement(Link, { className: "cta cta-link pf-v5-c-select__menu-item", to: "/manage/groups" },
172
+ React.createElement(Trans, null, "Manage groups")))))))));
44
173
  }
@@ -9,9 +9,28 @@
9
9
  .pf-v5-c-select__menu {
10
10
  z-index: 1;
11
11
  }
12
+ li.pf-v5-c-menu__list-item {
13
+ font-size: 16px;
14
+ font-weight: 400;
15
+ }
12
16
  }
13
17
 
14
18
  .pf-v5-c-menu__group-title {
15
19
  margin: 0;
16
20
  padding: 0;
17
21
  }
22
+
23
+ label.pf-v5-u-mb-sm.group-filter-label,
24
+ .case-list-account-filter-label,
25
+ .case-list-username-filter-label {
26
+ font-size: 14px !important;
27
+ }
28
+
29
+ .group-filter {
30
+ span.pf-v5-c-menu-toggle__text {
31
+ color: var(--pf-v5-global--palette--black-600);
32
+ }
33
+ #case-list-group-filter {
34
+ z-index: 9 !important;
35
+ }
36
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rh-support/cases",
3
- "version": "2.1.69",
3
+ "version": "2.1.70",
4
4
  "publishConfig": {
5
5
  "access": "public",
6
6
  "registry": "https://registry.npmjs.org"
@@ -43,8 +43,8 @@
43
43
  "@patternfly/patternfly": "5.1.0",
44
44
  "@patternfly/react-core": "5.1.1",
45
45
  "@patternfly/react-table": "5.1.1",
46
- "@rh-support/components": "2.1.53",
47
- "@rh-support/react-context": "2.1.59",
46
+ "@rh-support/components": "2.1.54",
47
+ "@rh-support/react-context": "2.1.60",
48
48
  "@rh-support/types": "2.0.3",
49
49
  "@rh-support/user-permissions": "2.1.39",
50
50
  "@rh-support/utils": "2.1.29",
@@ -96,5 +96,5 @@
96
96
  "defaults and supports es6-module",
97
97
  "maintained node versions"
98
98
  ],
99
- "gitHead": "d579286a00d09356cd07729b5750176ad8e3c090"
99
+ "gitHead": "acb84e1dd7cb6d68a81c0a0871e5a203b6d7a67b"
100
100
  }