diff --git a/src/pages/domain/BaseDomainMembersPage.tsx b/src/pages/domain/BaseDomainMembersPage.tsx index 1ea3f2d97bed..42f23773acca 100644 --- a/src/pages/domain/BaseDomainMembersPage.tsx +++ b/src/pages/domain/BaseDomainMembersPage.tsx @@ -128,35 +128,40 @@ function BaseDomainMembersPage({ const icons = useMemoizedLazyExpensifyIcons(['FallbackAvatar']); const illustrations = useMemoizedLazyIllustrations(['EmptyShelves']); - const data: MemberOption[] = accountIDs.map((accountID) => { - const details = personalDetails?.[accountID]; - const login = details?.login ?? ''; - const customProps = getCustomRowProps?.(accountID, login); - const isPendingActionDelete = customProps?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; - - return { - keyForList: String(accountID), - accountID, - login, - text: formatPhoneNumber(getDisplayNameOrDefault(details)), - alternateText: formatPhoneNumber(login), - icons: [ - { - source: details?.avatar ?? icons.FallbackAvatar, - name: formatPhoneNumber(login), - type: CONST.ICON_TYPE_AVATAR, - id: accountID, - }, - ], - rightElement: getCustomRightElement?.(accountID), - errors: getLatestError(customProps?.errors), - pendingAction: customProps?.pendingAction, - isInteractive: !isPendingActionDelete && !details?.isOptimisticPersonalDetail, - isDisabled: isPendingActionDelete, - isDisabledCheckbox: isPendingActionDelete || !!details?.isOptimisticPersonalDetail, - brickRoadIndicator: customProps?.brickRoadIndicator, - }; - }); + const data: MemberOption[] = accountIDs + .filter((accountID) => { + const details = personalDetails?.[accountID]; + return !!details?.login || !!details?.displayName; + }) + .map((accountID) => { + const details = personalDetails?.[accountID]; + const login = details?.login ?? ''; + const customProps = getCustomRowProps?.(accountID, login); + const isPendingActionDelete = customProps?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; + + return { + keyForList: String(accountID), + accountID, + login, + text: formatPhoneNumber(getDisplayNameOrDefault(details)), + alternateText: formatPhoneNumber(login), + icons: [ + { + source: details?.avatar ?? icons.FallbackAvatar, + name: formatPhoneNumber(login), + type: CONST.ICON_TYPE_AVATAR, + id: accountID, + }, + ], + rightElement: getCustomRightElement?.(accountID), + errors: getLatestError(customProps?.errors), + pendingAction: customProps?.pendingAction, + isInteractive: !isPendingActionDelete && !details?.isOptimisticPersonalDetail, + isDisabled: isPendingActionDelete, + isDisabledCheckbox: isPendingActionDelete || !!details?.isOptimisticPersonalDetail, + brickRoadIndicator: customProps?.brickRoadIndicator, + }; + }); const filterMember = (memberOption: MemberOption, searchQuery: string) => { const results = tokenizedSearch([memberOption], searchQuery, (option) => [option.text ?? '', option.alternateText ?? '']); diff --git a/src/selectors/Domain.ts b/src/selectors/Domain.ts index 7ded3405cb19..53e216ef9daa 100644 --- a/src/selectors/Domain.ts +++ b/src/selectors/Domain.ts @@ -84,7 +84,10 @@ function memberAccountIDsSelector(domain: OnyxEntry): number[] { const sharedMembers = securityGroup?.shared ?? {}; - for (const id of Object.keys(sharedMembers)) { + for (const [id, memberValue] of Object.entries(sharedMembers)) { + if (memberValue === null || memberValue === undefined) { + continue; + } const accountID = Number(id); if (!Number.isNaN(accountID)) { acc.push(accountID); diff --git a/tests/unit/DomainSelectorsTest.ts b/tests/unit/DomainSelectorsTest.ts index 21d7720759ce..243d8602aa95 100644 --- a/tests/unit/DomainSelectorsTest.ts +++ b/tests/unit/DomainSelectorsTest.ts @@ -265,6 +265,25 @@ describe('domainSelectors', () => { expect(memberAccountIDsSelector(domain)).toEqual([111]); }); + it('Should filter out members with null or undefined permission values', () => { + const domain = { + [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}1`]: { + shared: { + // eslint-disable-next-line @typescript-eslint/naming-convention + '100': 'read', + // eslint-disable-next-line @typescript-eslint/naming-convention + '200': null, + // eslint-disable-next-line @typescript-eslint/naming-convention + '300': undefined, + // eslint-disable-next-line @typescript-eslint/naming-convention + '400': 'read', + }, + }, + } as unknown as OnyxEntry; + + expect(memberAccountIDsSelector(domain).sort()).toEqual([100, 400]); + }); + it('Should filter out non-numeric shared keys', () => { const domain = { [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}1`]: {