Skip to content

Commit 5be0c62

Browse files
authored
fix(clerk-js): Always display Danger section in OrganizationSettings (#1679)
* fix(clerk-js): Always display Danger section in OrganizationSettings This eliminates layout shifts * test(clerk-js): Always display Danger section in OrganizationSettings * chore(clerk-js): Add changeset * chore(clerk-js): Change from nullish coalescing to logical or
1 parent e6a3889 commit 5be0c62

File tree

3 files changed

+90
-2
lines changed

3 files changed

+90
-2
lines changed

.changeset/quick-trees-poke.md

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
---
2+
---

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -101,12 +101,12 @@ const OrganizationDangerSection = () => {
101101
});
102102
const { navigate } = useRouter();
103103

104-
if (!organization || !membership || !adminMembers) {
104+
if (!organization || !membership) {
105105
return null;
106106
}
107107

108108
const adminDeleteEnabled = organization.adminDeleteEnabled;
109-
const hasMoreThanOneAdmin = adminMembers.length > 1;
109+
const hasMoreThanOneAdmin = (adminMembers?.length || 0) > 1;
110110
const isAdmin = membership.role === 'admin';
111111

112112
return (

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

+86
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,92 @@ describe('OrganizationSettings', () => {
8787
});
8888
});
8989

90+
describe('Danger section', () => {
91+
it('always displays danger section and the leave organization button', async () => {
92+
const { wrapper, fixtures } = await createFixtures(f => {
93+
f.withOrganizations();
94+
f.withUser({
95+
email_addresses: ['test@clerk.dev'],
96+
organization_memberships: [{ name: 'Org1', role: 'basic_member' }],
97+
});
98+
});
99+
100+
fixtures.clerk.organization?.getMemberships.mockReturnValue(Promise.resolve([]));
101+
const { getByText, queryByRole } = render(<OrganizationSettings />, { wrapper });
102+
await waitFor(() => {
103+
expect(fixtures.clerk.organization?.getMemberships).toHaveBeenCalled();
104+
expect(getByText('Danger')).toBeDefined();
105+
expect(getByText(/leave organization/i).closest('button')).toBeInTheDocument();
106+
expect(queryByRole('button', { name: /delete organization/i })).not.toBeInTheDocument();
107+
});
108+
});
109+
110+
it('enabled leave organization button with delete organization button', async () => {
111+
const adminsList: OrganizationMembershipResource[] = [
112+
createFakeMember({
113+
id: '1',
114+
orgId: '1',
115+
role: 'admin',
116+
}),
117+
createFakeMember({
118+
id: '2',
119+
orgId: '1',
120+
role: 'admin',
121+
}),
122+
];
123+
124+
const { wrapper, fixtures } = await createFixtures(f => {
125+
f.withOrganizations();
126+
f.withUser({
127+
email_addresses: ['test@clerk.dev'],
128+
organization_memberships: [{ name: 'Org1', role: 'admin', admin_delete_enabled: true }],
129+
});
130+
});
131+
132+
fixtures.clerk.organization?.getMemberships.mockReturnValue(Promise.resolve(adminsList));
133+
const { getByText } = render(<OrganizationSettings />, { wrapper });
134+
await waitFor(() => {
135+
expect(fixtures.clerk.organization?.getMemberships).toHaveBeenCalled();
136+
expect(getByText('Danger')).toBeDefined();
137+
expect(getByText(/leave organization/i).closest('button')).not.toHaveAttribute('disabled');
138+
expect(getByText(/delete organization/i).closest('button')).toBeInTheDocument();
139+
});
140+
});
141+
142+
it('disabled leave organization button with delete organization button', async () => {
143+
const adminsList: OrganizationMembershipResource[] = [
144+
createFakeMember({
145+
id: '1',
146+
orgId: '1',
147+
role: 'admin',
148+
}),
149+
createFakeMember({
150+
id: '2',
151+
orgId: '1',
152+
role: 'admin',
153+
}),
154+
];
155+
156+
const { wrapper, fixtures } = await createFixtures(f => {
157+
f.withOrganizations();
158+
f.withUser({
159+
email_addresses: ['test@clerk.dev'],
160+
organization_memberships: [{ name: 'Org1', role: 'admin', admin_delete_enabled: true }],
161+
});
162+
});
163+
164+
fixtures.clerk.organization?.getMemberships.mockReturnValue(Promise.resolve(adminsList));
165+
const { getByText, getByRole } = render(<OrganizationSettings />, { wrapper });
166+
await waitFor(() => {
167+
expect(fixtures.clerk.organization?.getMemberships).toHaveBeenCalled();
168+
expect(getByText('Danger')).toBeDefined();
169+
expect(getByText(/leave organization/i).closest('button')).toHaveAttribute('disabled');
170+
expect(getByText(/delete organization/i).closest('button')).toBeInTheDocument();
171+
expect(getByRole('button', { name: /delete organization/i })).toBeInTheDocument();
172+
});
173+
});
174+
});
175+
90176
describe('Navigation', () => {
91177
it('navigates to Organization Profile edit page when clicking on organization name and user is admin', async () => {
92178
const { wrapper, fixtures } = await createFixtures(f => {

0 commit comments

Comments
 (0)