Skip to content

Commit 68fe8b6

Browse files
authored
fix(clerk-js): Keep role select enabled with default org role (#7569)
1 parent 1a510b7 commit 68fe8b6

File tree

3 files changed

+77
-11
lines changed

3 files changed

+77
-11
lines changed

.changeset/dry-rings-report.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@clerk/clerk-js': patch
3+
---
4+
5+
Fix role select being disabled on `OrganizationProfile` invite members page when default role is not in roles list

packages/clerk-js/src/ui/components/OrganizationProfile/InviteMembersForm.tsx

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -48,19 +48,10 @@ export const InviteMembersForm = (props: InviteMembersFormProps) => {
4848
label: localizationKeys('formFieldLabel__emailAddresses'),
4949
});
5050

51-
const defaultRole = useDefaultRole();
5251
const roleField = useFormControl('role', '', {
5352
label: localizationKeys('formFieldLabel__role'),
5453
});
5554

56-
useEffect(() => {
57-
if (roleField.value || !defaultRole) {
58-
return;
59-
}
60-
61-
roleField.setValue(defaultRole);
62-
}, [defaultRole, roleField]);
63-
6455
if (!organization) {
6556
return null;
6657
}
@@ -200,8 +191,23 @@ export const InviteMembersForm = (props: InviteMembersFormProps) => {
200191

201192
const AsyncRoleSelect = (field: ReturnType<typeof useFormControl<'role'>>) => {
202193
const { options, isLoading, hasRoleSetMigration } = useFetchRoles();
203-
204194
const { t } = useLocalizations();
195+
const defaultRole = useDefaultRole();
196+
197+
useEffect(() => {
198+
if (field.value || !defaultRole) {
199+
return;
200+
}
201+
202+
// Skip if the default role from org settings is not in the current role set
203+
// This will eventually be returned by the roles endpoint, and `organizationSettings.domains.defaultRole` will be deprecated
204+
const defaultRoleExists = options?.some(option => option.value === defaultRole);
205+
if (!defaultRoleExists) {
206+
return;
207+
}
208+
209+
field.setValue(defaultRole);
210+
}, [defaultRole, options, field]);
205211

206212
return (
207213
<Form.ControlRow elementId={field.id}>

packages/clerk-js/src/ui/components/OrganizationProfile/__tests__/InviteMembersPage.test.tsx

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,59 @@ describe('InviteMembersPage', () => {
245245
await waitFor(() => expect(getByRole('button', { name: /select role/i })).toBeInTheDocument());
246246
});
247247

248+
it('enables selecting other options if default role is not available', async () => {
249+
const { wrapper, fixtures } = await createFixtures(f => {
250+
f.withOrganizations();
251+
f.withOrganizationDomains(undefined, 'mydefaultrole');
252+
f.withUser({
253+
email_addresses: ['[email protected]'],
254+
organization_memberships: [{ name: 'Org1', role: 'admin' }],
255+
});
256+
});
257+
258+
fixtures.clerk.organization?.getInvitations.mockRejectedValue(null);
259+
fixtures.clerk.organization?.getRoles.mockResolvedValue({
260+
total_count: 1,
261+
data: [
262+
{
263+
pathRoot: '',
264+
reload: vi.fn(),
265+
id: 'member',
266+
key: 'member',
267+
name: 'member',
268+
description: '',
269+
permissions: [],
270+
createdAt: new Date(),
271+
updatedAt: new Date(),
272+
},
273+
{
274+
pathRoot: '',
275+
reload: vi.fn(),
276+
id: 'admin',
277+
key: 'admin',
278+
name: 'admin',
279+
description: '',
280+
permissions: [],
281+
createdAt: new Date(),
282+
updatedAt: new Date(),
283+
},
284+
],
285+
});
286+
287+
fixtures.clerk.organization?.inviteMembers.mockResolvedValueOnce([{}] as OrganizationInvitationResource[]);
288+
const { findByText, getByRole, userEvent, getByTestId } = render(
289+
<Action.Root>
290+
<InviteMembersScreen />
291+
</Action.Root>,
292+
{ wrapper },
293+
);
294+
await userEvent.type(getByTestId('tag-input'), '[email protected],');
295+
await waitFor(() => expect(getByRole('button', { name: /select role/i })).toBeInTheDocument());
296+
await userEvent.click(getByRole('button', { name: /select role/i }));
297+
await userEvent.click(await findByText(/admin/i));
298+
await waitFor(() => expect(getByRole('button', { name: 'Send invitations' })).not.toBeDisabled());
299+
});
300+
248301
it('enables send button with default role once email address has been entered', async () => {
249302
const defaultRole = 'mydefaultrole';
250303

@@ -306,7 +359,9 @@ describe('InviteMembersPage', () => {
306359

307360
expect(getByRole('button', { name: 'Send invitations' })).toBeDisabled();
308361
await userEvent.type(getByTestId('tag-input'), '[email protected],');
309-
expect(getByRole('button', { name: 'Send invitations' })).not.toBeDisabled();
362+
await waitFor(() => {
363+
expect(getByRole('button', { name: 'Send invitations' })).not.toBeDisabled();
364+
});
310365
await userEvent.click(getByRole('button', { name: /mydefaultrole/i }));
311366
});
312367
});

0 commit comments

Comments
 (0)