diff --git a/src/authz-module/components/TableControlBar/MultipleChoiceFilter.test.tsx b/src/authz-module/components/TableControlBar/MultipleChoiceFilter.test.tsx index ab56c751..c735c30a 100644 --- a/src/authz-module/components/TableControlBar/MultipleChoiceFilter.test.tsx +++ b/src/authz-module/components/TableControlBar/MultipleChoiceFilter.test.tsx @@ -5,6 +5,7 @@ import MultipleChoiceFilter from './MultipleChoiceFilter'; describe('MultipleChoiceFilter', () => { const defaultProps = { + filterId: 'test-column', filterButtonText: 'Test Filter', filterChoices: [ { displayName: 'Option 1', value: 'option1' }, @@ -72,7 +73,7 @@ describe('MultipleChoiceFilter', () => { expect(defaultProps.setFilter).toHaveBeenCalledWith( ['groupA1'], { - groupName: 'test filter', + groupName: 'test-column', value: 'groupA1', displayName: 'Group A Option', }, @@ -101,7 +102,7 @@ describe('MultipleChoiceFilter', () => { expect(mockSetFilter).toHaveBeenCalledWith( ['option1'], { - groupName: 'test filter', + groupName: 'test-column', value: 'option1', displayName: 'Option 1', }, @@ -119,7 +120,7 @@ describe('MultipleChoiceFilter', () => { expect(mockSetFilter).toHaveBeenCalledWith( [], { - groupName: 'test filter', + groupName: 'test-column', value: 'option1', displayName: 'Option 1', }, @@ -137,7 +138,7 @@ describe('MultipleChoiceFilter', () => { expect(mockSetFilter).toHaveBeenCalledWith( ['option1', 'option2'], { - groupName: 'test filter', + groupName: 'test-column', value: 'option2', displayName: 'Option 2', }, diff --git a/src/authz-module/components/TableControlBar/MultipleChoiceFilter.tsx b/src/authz-module/components/TableControlBar/MultipleChoiceFilter.tsx index cb77b67c..62f3d1f4 100644 --- a/src/authz-module/components/TableControlBar/MultipleChoiceFilter.tsx +++ b/src/authz-module/components/TableControlBar/MultipleChoiceFilter.tsx @@ -3,11 +3,12 @@ import { } from '@openedx/paragon'; import { useIntl } from '@edx/frontend-platform/i18n'; import { FilterList, Info, Search } from '@openedx/paragon/icons'; -import { useState } from 'react'; +import { useState, useMemo } from 'react'; import messages from '../messages'; import { FilterChoice, MultipleChoiceFilterProps } from './types'; const MultipleChoiceFilter = ({ + filterId, filterButtonText, filterChoices, filterValue, @@ -22,9 +23,9 @@ const MultipleChoiceFilter = ({ const { formatMessage } = useIntl(); const checkedBoxes = filterValue || []; - const handleClickCheckbox = (value, displayName) => { + const handleClickCheckbox = (value: string, displayName: string) => { const newValue = { - groupName: filterButtonText?.toLocaleLowerCase() || '', + groupName: filterId, value, displayName, }; @@ -36,7 +37,7 @@ const MultipleChoiceFilter = ({ return setFilter(newCheckedBoxes, newValue); }; - const getGroupedChoices = () => { + const groupedChoices = useMemo(() => { const groupedFilterChoices = filterChoices.reduce((groups, choice) => { const groupName = choice.groupName || 'Ungrouped'; const icon = choice.groupIcon || undefined; @@ -49,9 +50,9 @@ const MultipleChoiceFilter = ({ description: choice.description, }); return groups; - }, new Map; icon?: any }>()); + }, new Map; icon?: React.ComponentType<{}> }>()); return Array.from(groupedFilterChoices.values()); - }; + }, [filterChoices]); return ( @@ -105,7 +106,7 @@ const MultipleChoiceFilter = ({ )) - : getGroupedChoices().map(({ groupName, icon, options }) => ( + : groupedChoices.map(({ groupName, icon, options }) => (
{icon && } diff --git a/src/authz-module/components/TableControlBar/OrgFilter.test.tsx b/src/authz-module/components/TableControlBar/OrgFilter.test.tsx index 228b9d69..c9eddeb0 100644 --- a/src/authz-module/components/TableControlBar/OrgFilter.test.tsx +++ b/src/authz-module/components/TableControlBar/OrgFilter.test.tsx @@ -19,6 +19,7 @@ jest.mock('@src/authz-module/data/hooks', () => ({ describe('OrgFilter', () => { const defaultProps = { + filterId: 'org', filterButtonText: 'Organizations', filterValue: [], setFilter: jest.fn(), diff --git a/src/authz-module/components/TableControlBar/OrgFilter.tsx b/src/authz-module/components/TableControlBar/OrgFilter.tsx index cc0cd52e..7094549b 100644 --- a/src/authz-module/components/TableControlBar/OrgFilter.tsx +++ b/src/authz-module/components/TableControlBar/OrgFilter.tsx @@ -8,7 +8,7 @@ import MultipleChoiceFilter from './MultipleChoiceFilter'; type OrgFilterProps = Omit; const OrgFilter = ({ - filterButtonText, filterValue, setFilter, disabled, + filterId, filterButtonText, filterValue, setFilter, disabled, }: OrgFilterProps) => { const [searchValue, setSearchValue] = React.useState(undefined); const { @@ -27,6 +27,7 @@ const OrgFilter = ({ return ( { const defaultProps = { + filterId: 'role', filterButtonText: 'Roles', filterValue: [], setFilter: jest.fn(), diff --git a/src/authz-module/components/TableControlBar/RolesFilter.tsx b/src/authz-module/components/TableControlBar/RolesFilter.tsx index d0e061e9..57532236 100644 --- a/src/authz-module/components/TableControlBar/RolesFilter.tsx +++ b/src/authz-module/components/TableControlBar/RolesFilter.tsx @@ -8,12 +8,13 @@ import { getRolesFiltersOptions } from '../constants'; type RolesFilterProps = Omit; const RolesFilter = ({ - filterButtonText, filterValue, setFilter, disabled, + filterId, filterButtonText, filterValue, setFilter, disabled, }: RolesFilterProps) => { const intl = useIntl(); const rolesOptions = useMemo(() => getRolesFiltersOptions(intl), [intl]); return ( ({ describe('ScopesFilter', () => { const defaultProps = { + filterId: 'scope', filterButtonText: 'Scopes', filterValue: [], setFilter: jest.fn(), diff --git a/src/authz-module/components/TableControlBar/ScopesFilter.tsx b/src/authz-module/components/TableControlBar/ScopesFilter.tsx index a93854a6..ef7e20e5 100644 --- a/src/authz-module/components/TableControlBar/ScopesFilter.tsx +++ b/src/authz-module/components/TableControlBar/ScopesFilter.tsx @@ -2,7 +2,7 @@ import { useMemo, useState } from 'react'; import { useIntl } from '@edx/frontend-platform/i18n'; import { LocationOn } from '@openedx/paragon/icons'; import { useScopes } from '@src/authz-module/data/hooks'; -import { DEFAULT_FILTER_PAGE_SIZE } from '@src/authz-module/constants'; +import { DEFAULT_FILTER_PAGE_SIZE, getScopeContextType } from '@src/authz-module/constants'; import { MultipleChoiceFilterProps } from './types'; import MultipleChoiceFilter from './MultipleChoiceFilter'; import { RESOURCE_ICONS } from '../constants'; @@ -11,24 +11,22 @@ import messages from '../messages'; type ScopesFilterProps = Omit; const ScopesFilter = ({ - filterButtonText, filterValue, setFilter, disabled, + filterId, filterButtonText, filterValue, setFilter, disabled, }: ScopesFilterProps) => { const { formatMessage } = useIntl(); const [searchValue, setSearchValue] = useState(undefined); const { data: scopesData } = useScopes({ search: searchValue, pageSize: DEFAULT_FILTER_PAGE_SIZE }); const filterChoices = useMemo(() => (scopesData?.pages?.flatMap((p) => p.results) ?? []).map((scope) => { - const scopeIcon = scope.externalKey?.startsWith('lib') ? RESOURCE_ICONS.LIBRARY : RESOURCE_ICONS.COURSE; - let groupName = formatMessage(messages['authz.team.members.table.group.courses']); - if (scope.externalKey?.startsWith('lib')) { - groupName = formatMessage(messages['authz.team.members.table.group.libraries']); - } + const isLibrary = getScopeContextType(scope.externalKey ?? '') === 'library'; return { displayName: scope.displayName, value: scope.externalKey, description: scope.org?.shortName, - groupName, - groupIcon: scopeIcon, + groupName: formatMessage(messages[isLibrary + ? 'authz.team.members.table.group.libraries' + : 'authz.team.members.table.group.courses']), + groupIcon: isLibrary ? RESOURCE_ICONS.LIBRARY : RESOURCE_ICONS.COURSE, }; }), [scopesData?.pages, formatMessage]); @@ -38,6 +36,7 @@ const ScopesFilter = ({ return ( void; + setFilter: (value: string | undefined) => void; placeholder: string; } diff --git a/src/authz-module/components/TableControlBar/TableControlBar.tsx b/src/authz-module/components/TableControlBar/TableControlBar.tsx index cc6bf9a5..c59294a4 100644 --- a/src/authz-module/components/TableControlBar/TableControlBar.tsx +++ b/src/authz-module/components/TableControlBar/TableControlBar.tsx @@ -23,18 +23,13 @@ import OrgFilter from './OrgFilter'; import ScopesFilter from './ScopesFilter'; import { FilterChoice } from './types'; +// Keyed by column id, which is what FilterChoice.groupName carries. const FILTER_CHIPS_ICONS = { role: Person, - organization: Business, + org: Business, scope: LocationOn, }; -const FILTER_GROUP_TO_ID = { - role: 'role', - organization: 'org', - scope: 'scope', -}; - interface TableControlBarProps { onFilterChange?: (filters: string[]) => void; } @@ -82,16 +77,17 @@ const TableControlBar = ({ onFilterChange }: TableControlBarProps) => { secondField: columnTextFilterHeaders[1] || '', }); - const handleCloseFilter = (filterName, filterValue) => { - const actualFilterId = FILTER_GROUP_TO_ID[filterName] || filterName; - const filterGroup = state.filters.find((filter) => filter.id === actualFilterId); + const handleCloseFilter = (filterId, filterValue) => { + const filterGroup = state.filters.find((filter) => filter.id === filterId); const newFilterValue = filterGroup?.value.filter(item => item !== filterValue) || []; setAllFilters(state.filters.map(item => ( - item.id !== actualFilterId ? item : { id: item.id, value: newFilterValue }))); + item.id !== filterId ? item : { id: item.id, value: newFilterValue }))); setChronologicalFilters((prevFilters) => prevFilters.filter((filter) => filter.value !== filterValue)); }; - const handleSetFilters = (setFilter) => (allFilters: string[], newFilter: FilterChoice) => { + const handleSetFilters = ( + setFilter: (filters: string[]) => void, + ) => (allFilters: string[], newFilter: FilterChoice) => { setFilter(allFilters); setChronologicalFilters((prevFilters) => { if (!prevFilters.find((filter) => filter.value === newFilter.value)) { @@ -115,6 +111,7 @@ const TableControlBar = ({ onFilterChange }: TableControlBarProps) => { @@ -125,6 +122,7 @@ const TableControlBar = ({ onFilterChange }: TableControlBarProps) => { @@ -135,6 +133,7 @@ const TableControlBar = ({ onFilterChange }: TableControlBarProps) => { @@ -145,6 +144,7 @@ const TableControlBar = ({ onFilterChange }: TableControlBarProps) => { filter.id === 'scope')?.value || null} diff --git a/src/authz-module/components/TableControlBar/types.ts b/src/authz-module/components/TableControlBar/types.ts index 686f1361..36ba3cef 100644 --- a/src/authz-module/components/TableControlBar/types.ts +++ b/src/authz-module/components/TableControlBar/types.ts @@ -7,6 +7,9 @@ export type FilterChoice = { }; export interface MultipleChoiceFilterProps { + /** Column id of the filtered column. Used (not the localized label) to + * identify the filter group on chips and when removing applied filters. */ + filterId: string; filterButtonText: string; filterChoices: Array; filterValue: string[] | undefined; diff --git a/src/authz-module/role-assignation-wizard/components/ScopeFilterBar.tsx b/src/authz-module/role-assignation-wizard/components/ScopeFilterBar.tsx index a6b547c1..5964e90a 100644 --- a/src/authz-module/role-assignation-wizard/components/ScopeFilterBar.tsx +++ b/src/authz-module/role-assignation-wizard/components/ScopeFilterBar.tsx @@ -47,6 +47,7 @@ const ScopeFilterBar = ({