diff --git a/.changeset/dry-rings-report.md b/.changeset/dry-rings-report.md new file mode 100644 index 00000000000..8cb3e91b2b3 --- /dev/null +++ b/.changeset/dry-rings-report.md @@ -0,0 +1,5 @@ +--- +'@clerk/clerk-js': patch +--- + +Fix role select being disabled on `OrganizationProfile` invite members page when default role is not in roles list diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/InviteMembersForm.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/InviteMembersForm.tsx index 7f9e848850b..3a8ecc68b8d 100644 --- a/packages/clerk-js/src/ui/components/OrganizationProfile/InviteMembersForm.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationProfile/InviteMembersForm.tsx @@ -48,19 +48,10 @@ export const InviteMembersForm = (props: InviteMembersFormProps) => { label: localizationKeys('formFieldLabel__emailAddresses'), }); - const defaultRole = useDefaultRole(); const roleField = useFormControl('role', '', { label: localizationKeys('formFieldLabel__role'), }); - useEffect(() => { - if (roleField.value || !defaultRole) { - return; - } - - roleField.setValue(defaultRole); - }, [defaultRole, roleField]); - if (!organization) { return null; } @@ -200,8 +191,23 @@ export const InviteMembersForm = (props: InviteMembersFormProps) => { const AsyncRoleSelect = (field: ReturnType>) => { const { options, isLoading, hasRoleSetMigration } = useFetchRoles(); - const { t } = useLocalizations(); + const defaultRole = useDefaultRole(); + + useEffect(() => { + if (field.value || !defaultRole) { + return; + } + + // Skip if the default role from org settings is not in the current role set + // This will eventually be returned by the roles endpoint, and `organizationSettings.domains.defaultRole` will be deprecated + const defaultRoleExists = options?.some(option => option.value === defaultRole); + if (!defaultRoleExists) { + return; + } + + field.setValue(defaultRole); + }, [defaultRole, options, field]); return ( diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/__tests__/InviteMembersPage.test.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/__tests__/InviteMembersPage.test.tsx index 076655f075f..dd13114cff8 100644 --- a/packages/clerk-js/src/ui/components/OrganizationProfile/__tests__/InviteMembersPage.test.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationProfile/__tests__/InviteMembersPage.test.tsx @@ -245,6 +245,59 @@ describe('InviteMembersPage', () => { await waitFor(() => expect(getByRole('button', { name: /select role/i })).toBeInTheDocument()); }); + it('enables selecting other options if default role is not available', async () => { + const { wrapper, fixtures } = await createFixtures(f => { + f.withOrganizations(); + f.withOrganizationDomains(undefined, 'mydefaultrole'); + f.withUser({ + email_addresses: ['test@clerk.com'], + organization_memberships: [{ name: 'Org1', role: 'admin' }], + }); + }); + + fixtures.clerk.organization?.getInvitations.mockRejectedValue(null); + fixtures.clerk.organization?.getRoles.mockResolvedValue({ + total_count: 1, + data: [ + { + pathRoot: '', + reload: vi.fn(), + id: 'member', + key: 'member', + name: 'member', + description: '', + permissions: [], + createdAt: new Date(), + updatedAt: new Date(), + }, + { + pathRoot: '', + reload: vi.fn(), + id: 'admin', + key: 'admin', + name: 'admin', + description: '', + permissions: [], + createdAt: new Date(), + updatedAt: new Date(), + }, + ], + }); + + fixtures.clerk.organization?.inviteMembers.mockResolvedValueOnce([{}] as OrganizationInvitationResource[]); + const { findByText, getByRole, userEvent, getByTestId } = render( + + + , + { wrapper }, + ); + await userEvent.type(getByTestId('tag-input'), 'test+1@clerk.com,'); + await waitFor(() => expect(getByRole('button', { name: /select role/i })).toBeInTheDocument()); + await userEvent.click(getByRole('button', { name: /select role/i })); + await userEvent.click(await findByText(/admin/i)); + await waitFor(() => expect(getByRole('button', { name: 'Send invitations' })).not.toBeDisabled()); + }); + it('enables send button with default role once email address has been entered', async () => { const defaultRole = 'mydefaultrole'; @@ -306,7 +359,9 @@ describe('InviteMembersPage', () => { expect(getByRole('button', { name: 'Send invitations' })).toBeDisabled(); await userEvent.type(getByTestId('tag-input'), 'test+1@clerk.com,'); - expect(getByRole('button', { name: 'Send invitations' })).not.toBeDisabled(); + await waitFor(() => { + expect(getByRole('button', { name: 'Send invitations' })).not.toBeDisabled(); + }); await userEvent.click(getByRole('button', { name: /mydefaultrole/i })); }); });