-
+
- Add workspace
+ Add organization {
focusableWhenDisabled
/>
-
- Add workspace
+
+ Add organization,
@@ -96,8 +96,8 @@ describe('Mosaic Menu', () => {
Actions
-
- Add workspace
+
+ Add organization
,
@@ -117,7 +117,7 @@ describe('Mosaic Menu', () => {
// `role="menu"` sits on the positioner (floating-ui owns it); the popup is the surface inside.
expect(screen.getByRole('menu')).toHaveClass('cl-menu-positioner');
expect(screen.getByRole('menu').querySelector('.cl-menu-popup')).toBeInTheDocument();
- expect(screen.getByRole('menuitem', { name: 'Add workspace' })).toHaveAttribute('data-color', 'neutral');
+ expect(screen.getByRole('menuitem', { name: 'Add organization' })).toHaveAttribute('data-color', 'neutral');
expect(screen.getByRole('separator')).toHaveClass('cl-menu-separator');
expect(screen.getByTestId('add-icon')).toBeInTheDocument();
});
@@ -262,11 +262,11 @@ describe('Mosaic Menu', () => {
-
+
- Add workspace
+ Add organization,
diff --git a/packages/mosaic/src/features/user-button/__tests__/user-button.feature.test.tsx b/packages/mosaic/src/features/user-button/__tests__/user-button.feature.test.tsx
index 08657d42c66..70c4d37400b 100644
--- a/packages/mosaic/src/features/user-button/__tests__/user-button.feature.test.tsx
+++ b/packages/mosaic/src/features/user-button/__tests__/user-button.feature.test.tsx
@@ -180,7 +180,7 @@ describe('UserButton', () => {
expect(within(requiredPopup()).getByText('Alice Smith')).toBeInTheDocument();
});
- describe('switching workspace', () => {
+ describe('switching organization', () => {
it('makes the selected organization active and closes', async () => {
const { navigate } = await renderUserButton();
const user = await open();
@@ -194,7 +194,7 @@ describe('UserButton', () => {
expect(navigate).not.toHaveBeenCalled();
});
- it('leaves the active organization for the personal workspace', async () => {
+ it('leaves the active organization for the personal account', async () => {
await renderUserButton();
const user = await open();
@@ -206,7 +206,7 @@ describe('UserButton', () => {
expect(screen.queryByRole('button', { name: 'Personal account' })).toBeNull();
});
- it('drops the personal workspace where the app hides it', async () => {
+ it('drops the personal account where the app hides it', async () => {
await renderUserButton({ hidePersonal: true });
await open();
@@ -214,7 +214,7 @@ describe('UserButton', () => {
expect(screen.queryByText('Personal account')).toBeNull();
});
- it('leads with the account, and lists no personal workspace, when the instance forces an organization and none is active', async () => {
+ it('leads with the account, and lists no personal account, when the instance forces an organization and none is active', async () => {
await renderUserButton(
{},
signedIn({
@@ -708,7 +708,7 @@ describe('UserButton', () => {
expect(within(requiredPopup()).getAllByText('alice')).toHaveLength(1);
});
- it('lists no workspaces and offers no way to make one', async () => {
+ it('lists no organizations and offers no way to make one', async () => {
await renderUserButton({ mode: 'user' });
await open();
@@ -774,7 +774,7 @@ describe('UserButton', () => {
expect(screen.queryByRole('button', { name: 'Manage organization' })).toBeNull();
});
- it('carries no account rows, and trails the workspaces with create-organization', async () => {
+ it('carries no account rows, and trails the organizations with create-organization', async () => {
await renderUserButton({ mode: 'organization' }, noOffers());
await openWithList();
@@ -815,7 +815,7 @@ describe('UserButton', () => {
]);
});
- it('trails the workspaces with create-organization', async () => {
+ it('trails the organizations with create-organization', async () => {
await renderUserButton({}, noOffers());
await openWithList();
@@ -902,8 +902,8 @@ describe('UserButton', () => {
});
});
- describe('the workspace list', () => {
- it('lists the active workspace first, then the rest held, then the invitations, then the suggestions', async () => {
+ describe('the organization list', () => {
+ it('lists the active organization first, then the rest held, then the invitations, then the suggestions', async () => {
await renderUserButton();
await openWithList();
@@ -916,14 +916,14 @@ describe('UserButton', () => {
]);
});
- it('names the active workspace as the current one', async () => {
+ it('names the active organization as the current one', async () => {
await renderUserButton();
await openWithList();
expect(current()).toEqual([expect.stringContaining('Acme')]);
});
- it('checks the personal workspace, and offers no switch to it, where it is active', async () => {
+ it('checks the personal account, and offers no switch to it, where it is active', async () => {
await renderUserButton({}, signedIn({ client: fapiClient([personalSession, bobSession]) }));
await openWithList();
@@ -931,7 +931,7 @@ describe('UserButton', () => {
expect(screen.queryByRole('button', { name: 'Personal account' })).toBeNull();
});
- it('describes each offer by the workspace it acts on', async () => {
+ it('describes each offer by the organization it acts on', async () => {
await renderUserButton();
await openWithList();
@@ -982,7 +982,7 @@ describe('UserButton', () => {
expect(reading('Beta', 'Requested')).toEqual(['Beta', 'Requested']);
});
- it('lists an accepted invitation once, as a workspace to switch to', async () => {
+ it('lists an accepted invitation once, as an organization to switch to', async () => {
await renderUserButton({}, noOffers({ invitations: [fapiInvitation('inv_1', gamma)] }));
const user = await openWithList();
diff --git a/packages/mosaic/src/features/user-button/user-button.layout.ts b/packages/mosaic/src/features/user-button/user-button.layout.ts
index bb3743f7f1c..346142dd0f9 100644
--- a/packages/mosaic/src/features/user-button/user-button.layout.ts
+++ b/packages/mosaic/src/features/user-button/user-button.layout.ts
@@ -17,9 +17,9 @@ import type { UserButtonData, UserButtonHeaderLayout, UserButtonMode } from './u
* │ ⤴ Sign out │ │ │ │ ⤴ Sign out │ ┘ footer
* └────────────────────────────┘ └──────────────────────────┘ └────────────────────────────┘
*
- * The organizations are listed on the surface, since they are the workspaces the active account can
+ * The organizations are listed on the surface, since they are what the active account can
* switch between. The other signed-in accounts are not: they are one row at the foot that opens a
- * flyout of them, so the surface stays about the workspace it is on.
+ * flyout of them, so the surface stays about the organization it is on.
*
* The header is about whatever leads, not the mode: an organization is managed and invited to, an
* account is managed, and signed out of at the foot. A combined surface always leads with the account. With an
@@ -43,7 +43,7 @@ export type UserButtonAction =
/**
* What the trigger names and the header leads with. `member` is the account, badged with the
* organization it is active in. `none` is an organization-led surface with no organization active
- * and no personal workspace to fall back to.
+ * and no personal account to fall back to.
*/
export type UserButtonLead = 'organization' | 'member' | 'user' | 'none';
@@ -57,9 +57,9 @@ const headers = {
/** One mode's whole surface below the header, top to bottom. */
interface ModeLayout {
/**
- * The workspaces the active account switches between: its own, plus the organizations it is in.
+ * The organizations the active account switches between: its personal account, plus the organizations it is in.
* `false` is a list the mode does not carry at all. `footer` trails the rows, inside the list,
- * since what it offers is one more workspace.
+ * since what it offers is one more organization.
*/
organizations: { footer: readonly UserButtonAction[] } | false;
/**
@@ -79,7 +79,7 @@ const modes = {
organizations: { footer: ['createOrganization'] },
footer: { multiSession: [], singleSession: [] },
},
- // No workspaces at all, so the foot is the whole of it.
+ // No organizations at all, so the foot is the whole of it.
user: {
organizations: false,
footer: { multiSession: ['switchAccount', 'signOut'], singleSession: ['addAccount', 'signOut'] },
@@ -92,7 +92,7 @@ const modes = {
*/
export interface UserButtonLayout {
lead: UserButtonLead;
- /** The organization rows: their own workspace, the organizations, and what is on offer. */
+ /** The organization rows: the personal account, the organizations, and what is on offer. */
showOrganizations: boolean;
headerLayout: UserButtonHeaderLayout;
/** What each slot carries, in the order it renders. */
diff --git a/packages/mosaic/src/features/user-button/user-button.messages.ts b/packages/mosaic/src/features/user-button/user-button.messages.ts
index 10f236ce4f9..f08288a4c54 100644
--- a/packages/mosaic/src/features/user-button/user-button.messages.ts
+++ b/packages/mosaic/src/features/user-button/user-button.messages.ts
@@ -5,7 +5,7 @@ export const userButtonMessages = {
popup: {
label: 'Account',
},
- workspaces: {
+ organizations: {
personal: 'Personal account',
notSelected: 'No organization selected',
loading: 'Loading organizations…',
diff --git a/packages/mosaic/src/features/user-button/user-button.model.tsx b/packages/mosaic/src/features/user-button/user-button.model.tsx
index b987d404d33..3c8e3739e97 100644
--- a/packages/mosaic/src/features/user-button/user-button.model.tsx
+++ b/packages/mosaic/src/features/user-button/user-button.model.tsx
@@ -241,7 +241,7 @@ export function useUserButtonModel(options?: UserButtonModelOptions, modals?: Us
: [],
);
- // Organization requests are scoped to the active session, so another account's workspaces are unknowable.
+ // Organization requests are scoped to the active session, so another account's organizations are unknowable.
const additionalSessions: UserButtonSession[] = (clerk.client?.signedInSessions ?? []).flatMap(s => {
const sessionUser = s.user;
if (!sessionUser || s.id === session.id) {
diff --git a/packages/mosaic/src/features/user-button/user-button.styles.ts b/packages/mosaic/src/features/user-button/user-button.styles.ts
index 7b58a96458b..29dce5c1688 100644
--- a/packages/mosaic/src/features/user-button/user-button.styles.ts
+++ b/packages/mosaic/src/features/user-button/user-button.styles.ts
@@ -61,8 +61,8 @@ export const styles = stylex.create({
minWidth: 0,
},
- // Matches `UserButtonItemLabel`, so the trigger names a workspace the same way its row does. Capped,
- // because the trigger sits in a host app's chrome and a long workspace name would push it apart.
+ // Matches `UserButtonItemLabel`, so the trigger names an organization the same way its row does. Capped,
+ // because the trigger sits in a host app's chrome and a long organization name would push it apart.
triggerName: {
color: colorVars['--cl-color-foreground'],
fontSize: typeScaleVars['--cl-text-sm-size'],
@@ -83,7 +83,7 @@ export const styles = stylex.create({
marginInlineEnd: space['1'],
},
- // The workspace list scrolls; the header and footer stay put. The scroll area carries the
+ // The organization list scrolls; the header and footer stay put. The scroll area carries the
// overflow, the edge fades, the scrollbar and the scroll padding they need, so only the cap
// is ours.
scroll: {
diff --git a/packages/mosaic/src/features/user-button/user-button.types.ts b/packages/mosaic/src/features/user-button/user-button.types.ts
index 4919aa512d9..a715f6bffdb 100644
--- a/packages/mosaic/src/features/user-button/user-button.types.ts
+++ b/packages/mosaic/src/features/user-button/user-button.types.ts
@@ -39,7 +39,7 @@ export interface UserButtonInvitation {
organizationId: string;
organizationName: string;
imageUrl?: string;
- /** `accepted` is already a workspace, so it lists as one rather than offering to be accepted. */
+ /** `accepted` is already a membership, so it lists as one rather than offering to be accepted. */
status: 'pending' | 'accepted';
}
@@ -54,22 +54,22 @@ export interface UserButtonData {
/**
* The active organization, described whole rather than found in `memberships`, so the surface
* names it while the list it belongs to is still loading. `null` means none is active: the
- * personal workspace when one exists, and no selection otherwise.
+ * personal account when one exists, and no selection otherwise.
*/
activeOrganization: UserButtonMembership | null;
/**
* Explicit; do not derive from `memberships.length`. Answered before the lists are fetched, so
- * the surface knows whether to carry a workspace section at all without waiting on them.
+ * the surface knows whether to carry an organization section at all without waiting on them.
*/
hasOrganizations: boolean;
/**
- * The account has no workspace of its own to return to, so the organizations are all there is.
+ * There is no personal account to return to, so the organizations are all there is.
* Withholds the personal row rather than standing it down: this is not a switch that is
- * momentarily unavailable, it is a workspace that does not exist here.
+ * momentarily unavailable, it is a personal account that does not exist here.
*/
hidePersonal?: boolean;
/**
- * A first page is still in flight, so the workspace rows stand in as one placeholder rather than
+ * A first page is still in flight, so the organization rows stand in as one placeholder rather than
* appearing a list at a time.
*/
organizationsLoading?: boolean;
@@ -88,7 +88,7 @@ export interface UserButtonData {
export interface UserButtonCallbacks {
/**
* Acts on the active account; another account's organizations are unreachable until you switch.
- * `null` selects the personal workspace, which is how an account leaves an organization.
+ * `null` selects the personal account, which is how an account leaves an organization.
*/
onSelectOrganization?: (organizationId: string | null) => void;
onAcceptSuggestion?: (suggestionId: string) => void;
@@ -111,7 +111,7 @@ export interface UserButtonCallbacks {
export type UserButtonMode = 'combined' | 'organization' | 'user';
/**
- * How the header carries its actions: `inline` trails the workspace with them, the gear as an icon;
+ * How the header carries its actions: `inline` trails the lead with them, the gear as an icon;
* `stacked` runs them under it as full-width labelled buttons.
*/
export type UserButtonHeaderLayout = 'inline' | 'stacked';
diff --git a/packages/mosaic/src/features/user-button/user-button.view.tsx b/packages/mosaic/src/features/user-button/user-button.view.tsx
index d7755ae6814..51791d865c7 100644
--- a/packages/mosaic/src/features/user-button/user-button.view.tsx
+++ b/packages/mosaic/src/features/user-button/user-button.view.tsx
@@ -98,7 +98,7 @@ function useBusy(key?: string): { busy: boolean; disabled: boolean } {
return { busy: pendingKey === key, disabled: pendingKey !== key };
}
-type ActiveWorkspace =
+type LeadSelection =
| {
kind: 'organization';
name: string;
@@ -111,14 +111,14 @@ type ActiveWorkspace =
/**
* What the surface leads with: named in the trigger and headed in the popup, so the two always
- * agree. An organization-led surface with no org and no personal workspace is no selection.
+ * agree. An organization-led surface with no org and no personal account is no selection.
*/
type Messages = MosaicMessages['userButton'];
-function leadWorkspace(
+function leadSelection(
{ layout, activeOrganization, activeSession }: UserButtonContextValue,
m: Messages,
-): ActiveWorkspace {
+): LeadSelection {
if (layout.lead === 'organization' && activeOrganization) {
return {
kind: 'organization',
@@ -129,7 +129,7 @@ function leadWorkspace(
};
}
if (layout.lead === 'none') {
- return { kind: 'none', name: m.workspaces.notSelected, shape: 'square' };
+ return { kind: 'none', name: m.organizations.notSelected, shape: 'square' };
}
return {
kind: 'user',
@@ -140,12 +140,12 @@ function leadWorkspace(
};
}
-/** The organization a workspace shows: the one it is, or the one badging the account. */
-function workspaceOrganization(workspace: ActiveWorkspace): UserButtonMembership | undefined {
- if (workspace.kind === 'organization') {
- return workspace.organization;
+/** The organization a lead shows: the one it is, or the one badging the account. */
+function leadOrganization(lead: LeadSelection): UserButtonMembership | undefined {
+ if (lead.kind === 'organization') {
+ return lead.organization;
}
- return workspace.kind === 'user' ? workspace.badge : undefined;
+ return lead.kind === 'user' ? lead.badge : undefined;
}
function joinDetails(...parts: Array): string {
@@ -154,7 +154,9 @@ function joinDetails(...parts: Array): string {
function membershipSubtitle(membership: UserButtonMembership, m: Messages, locale: string): string {
const members =
- membership.membersCount === undefined ? undefined : plural(m.workspaces.members, membership.membersCount, locale);
+ membership.membersCount === undefined
+ ? undefined
+ : plural(m.organizations.members, membership.membersCount, locale);
return joinDetails(membership.planLabel, members);
}
@@ -188,7 +190,7 @@ function PendingSpinner() {
);
@@ -205,7 +207,7 @@ interface SwitcherRowProps {
avatarName?: string;
imageUrl?: string;
shape: 'circle' | 'square';
- /** Names the title element, for a control in the row that has to point at the workspace it acts on. */
+ /** Names the title element, for a control in the row that has to point at the organization it acts on. */
labelId?: string;
active?: boolean;
onSelect?: () => void;
@@ -415,7 +417,7 @@ function HeaderActionButton({
onClick={onClick}
type='button'
isPending={busy}
- pendingLabel={m.workspaces.pending}
+ pendingLabel={m.organizations.pending}
spinDelay={{ delay: 0 }}
>
{leading}
@@ -424,7 +426,7 @@ function HeaderActionButton({
);
}
-/** The active workspace: who you are signed in as, and what you can do about it. */
+/** The lead: who you are signed in as, and what you can do about it. */
function Header() {
const m = useMessages('userButton');
const locale = useLocale();
@@ -432,15 +434,15 @@ function Header() {
const layout = data.layout.headerLayout;
const userLed = data.layout.lead === 'user';
const { identifier } = data.activeSession;
- const workspace = leadWorkspace(data, m);
- const { name } = workspace;
- const organization = workspaceOrganization(workspace);
+ const lead = leadSelection(data, m);
+ const { name } = lead;
+ const organization = leadOrganization(lead);
// An account with no name is titled by its identifier, and repeating it underneath says nothing.
// No selection is not the account, so it carries no identifier line either.
const subtitle =
- workspace.kind === 'organization'
- ? membershipSubtitle(workspace.organization, m, locale)
- : workspace.kind === 'user' && identifier !== name
+ lead.kind === 'organization'
+ ? membershipSubtitle(lead.organization, m, locale)
+ : lead.kind === 'user' && identifier !== name
? identifier
: '';
@@ -463,7 +465,7 @@ function Header() {
onClick: data.onManageOrganization,
});
}
- if (workspace.kind !== 'organization' && data.onManageAccount) {
+ if (lead.kind !== 'organization' && data.onManageAccount) {
settings.push({
name: m.manage.account,
label: m.manage.profileSettings,
@@ -497,11 +499,11 @@ function Header() {
layout={layout}
avatar={
}
title={name}
@@ -545,7 +547,7 @@ function MembershipRow({ membership, active, onSelect }: MembershipRowProps) {
}
/**
- * The account's own workspace, which is what "no active organization" is. Listed alongside the
+ * The personal account, which is what "no active organization" is. Listed alongside the
* organizations so switching into one is not a one-way door: `null` is how you leave.
*
* Named for what it is among organizations rather than for the account, the way the existing
@@ -564,7 +566,7 @@ function PersonalRow() {
return (
m.organizationId));
@@ -690,7 +692,7 @@ function PendingRows() {
{/* Invitations first: one is addressed to this account and joins on accept, where a
suggestion only files a request. Same order as the existing OrganizationSwitcher. */}
{invitations.map(i =>
- // Already joined, so it is a workspace like any other: click the row to switch to it.
+ // Already joined, so it is an organization like any other: click the row to switch to it.
i.status === 'accepted' ? (
acceptInvitation(i.id) : undefined}
/>
),
@@ -720,9 +722,9 @@ function PendingRows() {
busyKey={userButtonBusyKeys.acceptSuggestion(s.id)}
name={s.name}
imageUrl={s.imageUrl}
- actionLabel={m.workspaces.join}
+ actionLabel={m.organizations.join}
// An accepted suggestion is waiting on an admin, so it reports rather than re-offers.
- note={s.status === 'accepted' ? m.workspaces.requested : undefined}
+ note={s.status === 'accepted' ? m.organizations.requested : undefined}
onAccept={acceptSuggestion ? () => acceptSuggestion(s.id) : undefined}
/>
))}
@@ -732,7 +734,7 @@ function PendingRows() {
/**
* A signed-in account inside the accounts flyout: a menu item you pick to switch to, checked where
- * it is already the active one. Its workspaces cannot be listed here — they are scoped to the
+ * it is already the active one. Its organizations cannot be listed here — they are scoped to the
* session that fetches them — so switching is all it offers.
*/
function SessionMenuItem({ session, active }: { session: UserButtonSession; active: boolean }) {
@@ -885,13 +887,13 @@ function OrganizationListLoadingRow() {
- {m.workspaces.loading}
+ {m.organizations.loading}
);
}
-/** The workspaces the active account can switch between. This is the group that scrolls. */
+/** The organizations the active account can switch between. This is the group that scrolls. */
function OrganizationSection() {
const m = useMessages('userButton');
const data = useUserButtonContext();
@@ -943,7 +945,7 @@ function OrganizationSection() {
>
))}
{/* Trails the rows rather than sitting at the foot of the surface: what it offers is one
- more of the workspaces above it, not an action on the account. */}
+ more of the organizations above it, not an action on the account. */}
{actions}
>
@@ -1075,7 +1077,7 @@ export function UserButtonRoot(props: UserButtonRootProps): ReactElement {
export interface UserButtonTriggerProps {
/**
- * Names the active workspace beside its avatar — the organization wherever one heads the
+ * Names the lead beside its avatar — the organization wherever one heads the
* trigger, no selection when personal is hidden and none is active, the account otherwise, with
* its active organization beneath it. Turn it off for the avatar alone.
*
@@ -1091,17 +1093,17 @@ export interface UserButtonTriggerProps {
renderTriggerBadge?: boolean;
}
-/** The trigger: the active workspace's avatar, and what it is called. */
+/** The trigger: the lead's avatar, and what it is called. */
export function UserButtonTrigger({
renderTriggerLabel = true,
renderTriggerBadge = true,
}: UserButtonTriggerProps = {}): ReactElement {
const m = useMessages('userButton');
const data = useUserButtonContext();
- const workspace = leadWorkspace(data, m);
- const { name, shape } = workspace;
- const planLabel = renderTriggerBadge ? workspaceOrganization(workspace)?.planLabel : undefined;
- const badge = workspace.kind === 'user' ? workspace.badge : undefined;
+ const lead = leadSelection(data, m);
+ const { name, shape } = lead;
+ const planLabel = renderTriggerBadge ? leadOrganization(lead)?.planLabel : undefined;
+ const badge = lead.kind === 'user' ? lead.badge : undefined;
const ringsAvatar = !renderTriggerLabel && badge !== undefined;
return (
@@ -1116,9 +1118,9 @@ export function UserButtonTrigger({
]}
>
{
test('accepts nested overrides', () => {
const value: MosaicCatalog = {
- userButton: { trigger: { open: 'Menü für {name}' }, workspaces: { members: { few: '{count} Mitglieder' } } },
+ userButton: { trigger: { open: 'Menü für {name}' }, organizations: { members: { few: '{count} Mitglieder' } } },
};
void value;
});
@@ -18,7 +18,7 @@ describe('MosaicCatalog', () => {
test('accepts dot paths, mixed with nested', () => {
const value: MosaicCatalog = {
'userButton.popup.label': 'Konto',
- 'userButton.workspaces.members': { many: '{count} членов' },
+ 'userButton.organizations.members': { many: '{count} членов' },
userProfile: { label: 'Profil' },
};
void value;
@@ -53,7 +53,7 @@ describe('MosaicCatalog', () => {
};
const nested: MosaicCatalog = {
// @ts-expect-error plural forms cannot become a string
- userButton: { workspaces: { members: 'x' } },
+ userButton: { organizations: { members: 'x' } },
};
void [value, nested];
});
diff --git a/packages/swingset/src/stories/action-bar.stories.tsx b/packages/swingset/src/stories/action-bar.stories.tsx
index 3a45c541c85..2b9efb5a465 100644
--- a/packages/swingset/src/stories/action-bar.stories.tsx
+++ b/packages/swingset/src/stories/action-bar.stories.tsx
@@ -156,7 +156,7 @@ function MembersProfile({
value='members'
{...props}
>
- Workspace
+ Organization(null);
return (
- }>Manage workspace
+ }>Manage organization
) {
return (
);
}
@@ -41,10 +41,10 @@ export function Default(props: Record) {
export function Inline(props: Record) {
return (
diff --git a/packages/swingset/src/stories/localization.mdx b/packages/swingset/src/stories/localization.mdx
index 5ed13d582e7..2605118b503 100644
--- a/packages/swingset/src/stories/localization.mdx
+++ b/packages/swingset/src/stories/localization.mdx
@@ -86,7 +86,7 @@ is left unused, and a placeholder the values do not cover is rendered literally.
{
"userButton": {
"trigger": { "open": "Abrir el menú de la cuenta de {name}" },
- "workspaces": {
+ "organizations": {
"members": { "one": "{count} miembro", "other": "{count} miembros" }
}
},
@@ -167,7 +167,7 @@ function Trigger({ name, count }: { name: string; count: number }) {
const locale = useLocale();
return (
);
}
diff --git a/packages/swingset/src/stories/localization.stories.tsx b/packages/swingset/src/stories/localization.stories.tsx
index 3d88870ba96..22817c083fe 100644
--- a/packages/swingset/src/stories/localization.stories.tsx
+++ b/packages/swingset/src/stories/localization.stories.tsx
@@ -24,7 +24,7 @@ const esES: MosaicCatalog = {
userButton: {
trigger: { open: 'Abrir el menú de la cuenta de {name}' },
popup: { label: 'Cuenta' },
- workspaces: {
+ organizations: {
personal: 'Cuenta personal',
notSelected: 'Ninguna organización seleccionada',
loading: 'Cargando organizaciones…',
@@ -83,7 +83,7 @@ const clerkCloud = {
// A static user button: every string it renders comes from the `userButton` namespace, so it
// shows a catalog or an override the moment the popup opens.
-function Workspace() {
+function OrganizationUserButton() {
const [open, setOpen] = useState(false);
return (
30 } })],
]}
/>
@@ -145,7 +145,7 @@ function Strings() {
function Counts({ counts }: { counts: number[] }) {
const m = useMessages('userButton');
const locale = useLocale();
- return [String(count), plural(m.workspaces.members, count, locale)])} />;
+ return [String(count), plural(m.organizations.members, count, locale)])} />;
}
export function Overrides() {
@@ -158,7 +158,7 @@ export function Overrides() {
},
}}
>
-
+
);
}
@@ -166,7 +166,7 @@ export function Overrides() {
export function Catalog() {
return (
-
+
);
}
@@ -180,7 +180,7 @@ export function CatalogWithOverrides() {
overrides: { userButton: { manage: { invite: 'Invitar al equipo' } } },
}}
>
-
+
);
}
@@ -203,7 +203,7 @@ export function PluralRules() {
localization={{
locale: 'ru',
overrides: {
- 'userButton.workspaces.members': {
+ 'userButton.organizations.members': {
one: '{count} участник',
few: '{count} участника',
many: '{count} участников',
diff --git a/packages/swingset/src/stories/menu.component.mdx b/packages/swingset/src/stories/menu.component.mdx
index fe1b5d658a6..a5f1862dd7d 100644
--- a/packages/swingset/src/stories/menu.component.mdx
+++ b/packages/swingset/src/stories/menu.component.mdx
@@ -20,11 +20,11 @@ import { Menu } from '@clerk/mosaic/components/menu';
-
+
- Add workspace
+ Add organization
-
+
- Add workspace
+ Add organization
@@ -83,7 +83,7 @@ export function Accounts() {
);
}
-const workspaces = [
+const organizations = [
'Ashgrove',
'Bramblewood',
'Cedar Hollow',
@@ -154,22 +154,22 @@ const workspaces = [
export function Overflowing() {
return (
- Switch workspace
+ Switch organization
- {workspaces.map(workspace => (
+ {organizations.map(organization => (
- {workspace[0]}
+ {organization[0]}
- {workspace}
+ {organization}
))}
diff --git a/packages/swingset/src/stories/user-button.mdx b/packages/swingset/src/stories/user-button.mdx
index 8aa7f9f4864..9879dd2dd6d 100644
--- a/packages/swingset/src/stories/user-button.mdx
+++ b/packages/swingset/src/stories/user-button.mdx
@@ -3,15 +3,15 @@ import * as UserButtonStories from './user-button.stories';
# UserButton
The account & organization switcher behind the user avatar. The account heads the surface, badged
-with the active organization, carrying **Settings** and **Invite**. The account's workspaces sit under it: every organization, plus
+with the active organization, carrying **Settings** and **Invite**. The account's organizations sit under it: every one it belongs to, plus
**suggested** and **invited** ones it can Join. **Switch account** at the foot opens the signed-in
-accounts as a flyout — the active one checked — so an account never reads as a workspace. The
+accounts as a flyout — the active one checked — so an account never reads as an organization. The
flyout also adds an account and signs out of all of them. With nobody to switch to, that row is
**Add account** instead. The foot signs out of the active account. `mode` narrows all of it;
see [Modes](#modes).
Only the active account's organizations are listed. Not a design choice: org requests are scoped to
-the session that makes them, so another account's workspaces are unknowable until it _is_ active.
+the session that makes them, so another account's organizations are unknowable until it _is_ active.
Hence `additionalSessions` carries sessions alone, and `onSelectOrganization` takes no session.
Composed from `Popover`, `Card`, `Item`, `Avatar`, and `Menu`, so behavior, focus, and ARIA come from
@@ -21,7 +21,7 @@ it with `useUserButtonController()` and needs no props.
## Example
A working prototype over mock accounts — try it. Nothing is instant: the clicked row spins, the rest
-stand down, and the surface stays open so you land on the result. Only picking a workspace closes it.
+stand down, and the surface stays open so you land on the result. Only picking an organization closes it.
`pendingKey` drives that, and the connected `UserButton` sets it from the action in flight. Actions
that would navigate (Manage, Invite, Create organization, Add account) only close the popover here.
@@ -97,7 +97,7 @@ fully to match it; see [User only](#user-only).
The header names whatever leads and carries what you can do about it, stacked under it as
full-width labelled buttons. An organization is described by its plan and member count, and carries
**Settings** with **Invite**. An account is described by its identifier. Where **Settings** is the only
-action, as it is for an account, it trails the workspace inline.
+action, as it is for an account, it trails the account inline.
With an organization active in `combined`, **Settings** opens a menu of **Organization settings** and
**Profile settings**. Where only one of them is available, **Settings** opens it directly.
@@ -117,7 +117,7 @@ What a mode leads with is named in the trigger and heads the popup — the two a
### Combined
-The account heads it, badged with the active org, with **Settings** and **Invite**. The workspaces
+The account heads it, badged with the active org, with **Settings** and **Invite**. The organizations
sit under it, with **Create organization** at their foot. **Switch account** opens the signed-in
accounts, with **Add account** and **Sign out of all accounts**, and **Sign out** at the foot signs
out of the active account.
@@ -141,7 +141,7 @@ With one account signed in, the foot is **Add account** and **Sign out**.
### Org only
-The org heads it, with **Settings** and **Invite**. **Create organization** trails the workspaces, and
+The org heads it, with **Settings** and **Invite**. **Create organization** trails the organizations, and
nothing else is on offer, so the popup ends there.
;
+type AccountOrganizations = Omit;
/** The multi-account frames: NestLabs Creative active, beside Acme, with Clerk on offer. */
-const nestLabsWithSuggestion: Workspaces = {
+const nestLabsWithSuggestion: AccountOrganizations = {
activeOrganizationId: nestLabs.organizationId,
memberships: [nestLabs, acme],
suggestions: [clerkSuggestion],
@@ -93,25 +93,25 @@ const nestLabsWithSuggestion: Workspaces = {
};
/** The single-account and organization-only frames: NestLabs Creative active, beside Clerk App and Acme. */
-const nestLabsWithClerkApp: Workspaces = {
+const nestLabsWithClerkApp: AccountOrganizations = {
activeOrganizationId: nestLabs.organizationId,
memberships: [nestLabs, clerkApp, acme],
suggestions: [],
invitations: [],
};
-/** The personal-workspace frame: no organization active, Clerk App and Acme to switch to. */
-const personalWorkspace: Workspaces = {
+/** The personal-account frame: no organization active, Clerk App and Acme to switch to. */
+const personalAccount: AccountOrganizations = {
activeOrganizationId: null,
memberships: [clerkApp, acme],
suggestions: [],
invitations: [],
};
-// The other two sign-ins' workspaces, reached only by switching to them.
+// The other two sign-ins' organizations, reached only by switching to them.
const otherAccounts: Account[] = [
{ session: cameron('sess_cameron_2'), ...nestLabsWithClerkApp },
- { session: cameron('sess_cameron_3'), ...personalWorkspace },
+ { session: cameron('sess_cameron_3'), ...personalAccount },
];
function chaosAccounts(accounts: Account[]): Account[] {
@@ -129,7 +129,7 @@ function chaosAccounts(accounts: Account[]): Account[] {
}));
}
-/** Joining is what turns a suggestion or an invitation into a workspace you can switch to. */
+/** Joining is what turns a suggestion or an invitation into an organization you can switch to. */
function join(account: Account, organizationId: string, name: string, imageUrl?: string): Account {
return {
...account,
@@ -144,26 +144,26 @@ function join(account: Account, organizationId: string, name: string, imageUrl?:
const LATENCY_MS = 800;
/**
- * The examples are prototypes, not screenshots: every row is wired to state, so picking a workspace
- * or an account really switches to it, and Join turns a suggestion into a workspace. None of it is
+ * The examples are prototypes, not screenshots: every row is wired to state, so picking an organization
+ * or an account really switches to it, and Join turns a suggestion into an organization. None of it is
* instant — each action is a network round trip against Clerk, so the prototype fakes one: the
* clicked row spins, the rest stand down, and the surface stays open so you land back on the result.
- * Only picking a workspace closes it, because that is the one action the surface exists to perform.
+ * Only picking an organization closes it, because that is the one action the surface exists to perform.
* The actions that would navigate somewhere in a real app (Manage, Invite, Create organization, Add
* account) have nowhere to go here, so they only close the popover.
*/
function usePrototype({
- workspaces = nestLabsWithSuggestion,
+ organizations = nestLabsWithSuggestion,
singleSession = false,
hidePersonal = false,
}: {
- workspaces?: Workspaces;
+ organizations?: AccountOrganizations;
singleSession?: boolean;
hidePersonal?: boolean;
} = {}): Omit {
const [open, setOpen] = useState(false);
const seed = useChaosFixture(
- [{ session: cameron('sess_cameron_1'), ...workspaces }, ...(singleSession ? [] : otherAccounts)],
+ [{ session: cameron('sess_cameron_1'), ...organizations }, ...(singleSession ? [] : otherAccounts)],
chaosAccounts,
);
const [accounts, setAccounts] = useState(seed);
@@ -280,7 +280,7 @@ export function CombinedIconTrigger(_args: Record) {
}
export function CombinedSingleAccount(_args: Record) {
- const prototype = usePrototype({ workspaces: nestLabsWithClerkApp, singleSession: true, hidePersonal: true });
+ const prototype = usePrototype({ organizations: nestLabsWithClerkApp, singleSession: true, hidePersonal: true });
return (
) {
// ─── Mode: Org only ─────────────────────────────────────────────────────────
export function Organizations(_args: Record) {
- const prototype = usePrototype({ workspaces: nestLabsWithClerkApp, hidePersonal: true });
+ const prototype = usePrototype({ organizations: nestLabsWithClerkApp, hidePersonal: true });
return (
) {
}
export function OrganizationsIconTrigger(_args: Record) {
- const prototype = usePrototype({ workspaces: nestLabsWithClerkApp, hidePersonal: true });
+ const prototype = usePrototype({ organizations: nestLabsWithClerkApp, hidePersonal: true });
return (
) {
}
export function NoOrganizationSelected(_args: Record) {
- const prototype = usePrototype({ workspaces: personalWorkspace, hidePersonal: true });
+ const prototype = usePrototype({ organizations: personalAccount, hidePersonal: true });
// Personal is withheld and nothing is active, so the lead is no selection — not the account.
// Picking an organization leaves it.
@@ -447,7 +447,7 @@ export function CustomMenuItems(_args: Record) {
const ORGANIZATIONS_LATENCY_MS = 2500;
/**
- * The workspace list's first page landing under a popup that is already open. The other examples
+ * The organization list's first page landing under a popup that is already open. The other examples
* are handed their organizations on the first render, so the placeholder they would show has
* nothing to show it. The wait restarts on every open, so it can be watched more than once.
*/