diff --git a/.changeset/sixty-taxis-watch.md b/.changeset/sixty-taxis-watch.md
new file mode 100644
index 0000000000..f451cc149b
--- /dev/null
+++ b/.changeset/sixty-taxis-watch.md
@@ -0,0 +1,6 @@
+---
+'@twilio-paste/status': patch
+'@twilio-paste/core': patch
+---
+
+[Status] paired iconography should be decorative and paired with visible, descriptive text
diff --git a/cypress/integration/sitemap-vrt/constants.ts b/cypress/integration/sitemap-vrt/constants.ts
index f17c50b6bf..542fef0bc7 100644
--- a/cypress/integration/sitemap-vrt/constants.ts
+++ b/cypress/integration/sitemap-vrt/constants.ts
@@ -15,6 +15,7 @@ export const SITEMAP = [
'/blog/2023-01-27-paste-newsletter/',
'/blog/2023-04-13-paste-newsletter/',
'/blog/2023-06-12-paste-newsletter/',
+ '/components/account-switcher/',
'/components/aspect-ratio/',
'/components/anchor/',
'/components/alert-dialog/',
@@ -61,6 +62,7 @@ export const SITEMAP = [
'/components/menu/',
'/components/paragraph/',
'/components/popover/',
+ '/components/product-switcher/',
'/components/radio-group/',
'/components/radio-button-group/',
'/components/screen-reader-only/',
diff --git a/internal-docs/engineering/developer-workflow.md b/internal-docs/engineering/developer-workflow.md
index 84d684506d..6c951319ca 100644
--- a/internal-docs/engineering/developer-workflow.md
+++ b/internal-docs/engineering/developer-workflow.md
@@ -30,7 +30,7 @@ All our work is stored in Jira and we have an [active sprint](https://issues.cor
We work in 2 week sprints.
-Pick a ticket that you feel comfortable working on, assign it to yourself to let others know you’re working on it, and pull it into the “In Progress” column. Try to limit in-progress tickets to 1 or 2 at a time.
+Pick a ticket that you feel comfortable working on, assign it to yourself to let others know you’re working on it, and pull it into the “In-progress” column. Try to limit in-progress tickets to 1 or 2 at a time.
## Branching
diff --git a/packages/paste-core/components/status/src/constants.tsx b/packages/paste-core/components/status/src/constants.tsx
index a71e22ad9e..c6ee238a92 100644
--- a/packages/paste-core/components/status/src/constants.tsx
+++ b/packages/paste-core/components/status/src/constants.tsx
@@ -17,78 +17,62 @@ import type {StatusBadges} from './types';
export const StatusObject: StatusBadges = {
ProcessError: {
badgeVariant: 'default',
- icon: ,
+ icon: ,
color: 'colorTextError',
},
ProcessWarning: {
badgeVariant: 'default',
- icon: ,
+ icon: ,
color: 'colorTextWarning',
},
ProcessSuccess: {
badgeVariant: 'default',
- icon: ,
+ icon: ,
color: 'colorTextSuccess',
},
ProcessNeutral: {
badgeVariant: 'default',
- icon: ,
+ icon: ,
color: 'colorTextNeutral',
},
ProcessInProgress: {
badgeVariant: 'default',
- icon: (
-
- ),
+ icon: ,
color: 'colorTextNeutral',
},
ProcessDisabled: {
badgeVariant: 'decorative10',
- icon: ,
+ icon: ,
color: 'colorTextWeak',
},
ProcessDraft: {
badgeVariant: 'decorative10',
- icon: ,
+ icon: ,
color: 'colorTextWeak',
},
ConnectivityAvailable: {
badgeVariant: 'default',
- icon: (
-
- ),
+ icon: ,
color: 'colorTextSuccess',
},
ConnectivityBusy: {
badgeVariant: 'default',
- icon: ,
+ icon: ,
color: 'colorTextWarning',
},
ConnectivityUnavailable: {
badgeVariant: 'default',
- icon: (
-
- ),
+ icon: ,
color: 'colorTextError',
},
ConnectivityNeutral: {
badgeVariant: 'default',
- icon: ,
+ icon: ,
color: 'colorTextNeutral',
},
ConnectivityOffline: {
badgeVariant: 'decorative10',
- icon: ,
+ icon: ,
color: 'colorTextWeak',
},
} as const;
diff --git a/packages/paste-core/components/status/stories/StatusMenu.stories.tsx b/packages/paste-core/components/status/stories/StatusMenu.stories.tsx
index a0a1a1d64f..1dad106c10 100644
--- a/packages/paste-core/components/status/stories/StatusMenu.stories.tsx
+++ b/packages/paste-core/components/status/stories/StatusMenu.stories.tsx
@@ -40,7 +40,7 @@ const ProcessStatusMenu: React.FCError
- In progress
+ In-progressDisabled
@@ -201,7 +201,7 @@ const ProcessObject = {
},
InProgress: {
variant: 'ProcessInProgress',
- children: 'In progress',
+ children: 'In-progress',
},
Disabled: {
variant: 'ProcessDisabled',
@@ -315,7 +315,7 @@ export const Process: StoryFn = () => (
WarningSuccessNeutral
- In Progress
+ In-progressDisabledDraft
diff --git a/packages/paste-website/package.json b/packages/paste-website/package.json
index 150111cf3c..084774ee39 100644
--- a/packages/paste-website/package.json
+++ b/packages/paste-website/package.json
@@ -27,6 +27,7 @@
"@next/bundle-analyzer": "^13.1.6",
"@next/mdx": "^13.1.6",
"@sparticuz/chromium": "^110.0.0",
+ "@twilio-paste/account-switcher": "^1.0.0",
"@twilio-paste/alert": "^13.0.0",
"@twilio-paste/alert-dialog": "^8.0.0",
"@twilio-paste/anchor": "^11.0.0",
@@ -89,6 +90,7 @@
"@twilio-paste/pagination": "^6.0.0",
"@twilio-paste/paragraph": "^9.0.0",
"@twilio-paste/popover": "^12.0.0",
+ "@twilio-paste/product-switcher": "1.0.0",
"@twilio-paste/radio-button-group": "^3.0.0",
"@twilio-paste/radio-group": "^12.0.0",
"@twilio-paste/react-textarea-autosize-library": "^2.0.0",
diff --git a/packages/paste-website/src/component-examples/AccountSwitcherExamples.ts b/packages/paste-website/src/component-examples/AccountSwitcherExamples.ts
new file mode 100644
index 0000000000..3f668f8559
--- /dev/null
+++ b/packages/paste-website/src/component-examples/AccountSwitcherExamples.ts
@@ -0,0 +1,63 @@
+export const accountSwitcherExample = `
+const AccountSwitcherMenu = () => {
+ const accountSwitcher = useAccountSwitcherState();
+ const [selectedAccount, setSelectedAccount] = React.useState('Owl Telehealth');
+ return (
+ <>
+
+ Owl Telehealth
+
+
+
+ setSelectedAccount('Owl Telehealth')}
+ {...accountSwitcher}
+ >
+ Owl Telehealth
+
+ setSelectedAccount('Owl Health Demo')}
+ {...accountSwitcher}
+ >
+ Owl Health Demo
+
+ setSelectedAccount('Owl Subway')}
+ {...accountSwitcher}
+ >
+ Owl Subway
+
+
+
+
+ Account settings
+
+
+
+ View all accounts
+
+
+ View all subaccounts
+
+
+
+ Admin Center
+
+
+ >
+ );
+};
+
+render(
+
+)
+`.trim();
diff --git a/packages/paste-website/src/component-examples/DescriptionListExamples.ts b/packages/paste-website/src/component-examples/DescriptionListExamples.ts
index 03e6e4e2f8..960fcc7bf7 100644
--- a/packages/paste-website/src/component-examples/DescriptionListExamples.ts
+++ b/packages/paste-website/src/component-examples/DescriptionListExamples.ts
@@ -53,8 +53,8 @@ const DescriptionListExample = () => {
- In progress
+ title="In-progress" />
+ In-progress
diff --git a/packages/paste-website/src/component-examples/ProductSwitcherExamples.ts b/packages/paste-website/src/component-examples/ProductSwitcherExamples.ts
new file mode 100644
index 0000000000..f8ffe507fa
--- /dev/null
+++ b/packages/paste-website/src/component-examples/ProductSwitcherExamples.ts
@@ -0,0 +1,77 @@
+export const productSwitcherExample = `
+const ProductSwitcherMenu = () => {
+ const productSwitcher = useProductSwitcherState();
+ const [product, setProduct] = React.useState('twilio');
+ return (
+ <>
+
+
+ {
+ setProduct('twilio');
+ }}
+ productName="Twilio"
+ productStrapline="SMS, Voice & Video"
+ productIcon={}
+ />
+ {
+ setProduct('segment');
+ }}
+ productName="Segment"
+ productStrapline="Customer data platform"
+ productIcon={}
+ />
+ {
+ setProduct('flex');
+ }}
+ productName="Flex"
+ productStrapline="Cloud-based contact center"
+ productIcon={}
+ />
+ {
+ setProduct('sendgrid');
+ }}
+ productName="SendGrid"
+ productStrapline="Email delivery and API"
+ productIcon={}
+ />
+ {
+ setProduct('admin');
+ }}
+ productName="Console Admin"
+ productStrapline="Admin center"
+ productIcon={}
+ />
+
+ >
+ );
+};
+
+render(
+
+)
+`.trim();
diff --git a/packages/paste-website/src/component-examples/StatusMenuExamples.ts b/packages/paste-website/src/component-examples/StatusMenuExamples.ts
index 24ceabd4dd..2db293def4 100644
--- a/packages/paste-website/src/component-examples/StatusMenuExamples.ts
+++ b/packages/paste-website/src/component-examples/StatusMenuExamples.ts
@@ -17,7 +17,7 @@ export const ProcessObject = {
},
InProgress: {
variant: 'ProcessInProgress',
- children: 'In progress',
+ children: 'In-progress',
},
Disabled: {
variant: 'ProcessDisabled',
@@ -27,7 +27,7 @@ export const ProcessObject = {
variant: 'ProcessDraft',
children: 'Draft',
},
-};
+} as const;
export const processStatusMenuExample = `
const ProcessStatusMenu = () => {
const [process, setProcess] = React.useState(ProcessObject.Success);
@@ -42,28 +42,77 @@ const ProcessStatusMenu = () => {
{process.children}
- onClick('Success')} variant="default">
- Complete
-
- onClick('Neutral')} variant="default">
- In review
-
- onClick('Warning')} variant="default">
- Needs attention
-
- onClick('Error')} variant="default">
- Rejected
-
- onClick('InProgress')} variant="default">
- In progress
-
- onClick('Disabled')} variant="default">
- Paused
-
- onClick('Draft')} variant="default">
- Draft
-
-
+ onClick('Success')}
+ variant="default"
+ >
+ {ProcessObject.Success.children}
+
+ onClick('Neutral')}
+ variant="default"
+ >
+ {ProcessObject.Neutral.children}
+
+ onClick('Warning')}
+ variant="default"
+ >
+ {ProcessObject.Warning.children}
+
+ onClick('Error')}
+ variant="default"
+ >
+ {ProcessObject.Error.children}
+
+ onClick('InProgress')}
+ variant="default"
+ >
+ {ProcessObject.InProgress.children}
+
+ onClick('Disabled')}
+ variant="default"
+ >
+ {ProcessObject.Disabled.children}
+
+ onClick('Draft')}
+ variant="default"
+ >
+ {ProcessObject.Draft.children}
+
+
>
);
};
diff --git a/packages/paste-website/src/component-examples/StatusPatternExamples.tsx b/packages/paste-website/src/component-examples/StatusPatternExamples.tsx
index 4da8794bd5..0edb31a7f2 100644
--- a/packages/paste-website/src/component-examples/StatusPatternExamples.tsx
+++ b/packages/paste-website/src/component-examples/StatusPatternExamples.tsx
@@ -34,7 +34,7 @@ export const ProcessNeutral = (): JSX.Element => {
};
export const ProcessInProgress = (): JSX.Element => {
- return ;
+ return ;
};
export const ProcessDisabled = (): JSX.Element => {
@@ -103,8 +103,8 @@ export const processStatusExamples = `
- In Progress
+ title="In-progress" />
+ In-progress
{shouldShowStatusBadge && }
{shouldShowFigmaNeeded && (
-
-
- Design assets pending
-
+
+ Design assets pending
+
)}
{shouldShowPeerReviewNeeded ? (
-
-
- Peer review pending
-
+
+ Peer review pending
+
) : null}
);
diff --git a/packages/paste-website/src/components/site-wrapper/sidebar/SidebarNavigation.tsx b/packages/paste-website/src/components/site-wrapper/sidebar/SidebarNavigation.tsx
index 88837f7790..1de1f9fb2a 100644
--- a/packages/paste-website/src/components/site-wrapper/sidebar/SidebarNavigation.tsx
+++ b/packages/paste-website/src/components/site-wrapper/sidebar/SidebarNavigation.tsx
@@ -320,6 +320,30 @@ const SidebarNavigation = (): JSX.Element => {
);
}
+ if (name === 'Status') {
+ return (
+
+ event({
+ category: 'Left Navigation',
+ action: `click-${name}`,
+ label: name,
+ })
+ }
+ >
+
+ Status Badge
+
+
+ Status Menu
+
+
+ );
+ }
return (
{name}
diff --git a/packages/paste-website/src/pages/components/account-switcher/index.mdx b/packages/paste-website/src/pages/components/account-switcher/index.mdx
new file mode 100644
index 0000000000..7036ad1ba2
--- /dev/null
+++ b/packages/paste-website/src/pages/components/account-switcher/index.mdx
@@ -0,0 +1,349 @@
+export const meta = {
+ title: 'Account Switcher - Components',
+ package: '@twilio-paste/account-switcher',
+ description: "An Account Switcher is a stylized Menu Badge with a list of actions related to a user's accounts.",
+ slug: '/components/account-switcher/',
+};
+
+import Changelog from '@twilio-paste/account-switcher/CHANGELOG.md';
+import {
+ AccountSwitcher,
+ AccountSwitcherBadge,
+ AccountSwitcherGroup,
+ AccountSwitcherItem,
+ AccountSwitcherItemRadio,
+ AccountSwitcherSeparator,
+ useAccountSwitcherState,
+} from '@twilio-paste/account-switcher';
+
+import {Box} from '@twilio-paste/box';
+
+import {SidebarCategoryRoutes} from '../../../constants';
+import packageJson from '@twilio-paste/account-switcher/package.json';
+import DefaultLayout from '../../../layouts/DefaultLayout';
+import {getFeature, getNavigationData} from '../../../utils/api';
+import {accountSwitcherExample} from '../../../component-examples/AccountSwitcherExamples';
+
+export default DefaultLayout;
+
+export const getStaticProps = async () => {
+ const navigationData = await getNavigationData();
+ const feature = await getFeature('Account Switcher');
+ return {
+ props: {
+ data: {
+ ...packageJson,
+ ...feature,
+ },
+ navigationData,
+ },
+ };
+};
+
+
+
+---
+
+
+
+
+
+
+
+
+ {accountSwitcherExample}
+
+
+## Guidelines
+
+## About Account Switcher
+
+Account Switcher can be used anywhere a user needs to switch between accounts or sub-accounts, and can usually be found in the Topbar. Other names for accounts in Twilio products include "workspaces" and "projects".
+
+Account Switcher is a stylized [Menu Badge](/components/menu#menu-badge), with groups of account options displayed in the menu item list. The Menu Badge itself should display the current account the user is viewing and update if a new account is selected.
+
+Examples of account-related options include:
+
+- Navigating to recently visited accounts
+- Creating an account
+- Viewing all of a user’s accounts
+
+Account Switcher is an implementation of the [Menu](/components/menu) component and shares a lot of its sub-components and hooks. That means it shares a lot of the API.
+
+### Accessibility
+
+The Account Switcher implements a grouped Menu and supports all the same keyboard navigation that the [Menu](/components/menu) component does.
+
+## Examples
+
+
+ {accountSwitcherExample}
+
+
+---
+
+## Composition Notes
+
+The Account Switcher is made up of the following sub-components:
+
+- `AccountSwitcherBadge`
+- `AccountSwitcher`
+- `AccountSwitcherGroup`
+- `AccountSwitcherItem`
+- `AccountSwitcherItemRadio`
+- `AccountSwitcherSeparator`
+- `useAccountSwitcherState`
+
+### Account Switcher Badge
+
+Used to display the name of the current account selected or being viewed, and contains the menu trigger. The text inside the badge should update when a new account is selected.
+
+### Account Switcher
+
+Container for Account Switcher menu items.
+
+### Account Switcher Group
+
+Used to group similar items together in the Account Switcher menu. An example of this might be a list of recent accounts.
+
+### Account Switcher Item
+
+A menu item that can either perform an action or navigate to a new URL.
+
+### Account Switcher ItemRadio
+
+A menu item that can perform a single selection of an item within a named group. Similar to a radio button group, only one item can be selected at a time. Each item in the group should have a `name` and `value` and must be contained in a Group.
+
+### Account Switcher Separator
+
+Simple horizontal rule used to separate groups or individual items.
+
+### useAccountSwitcherState hook
+
+React hook used to initialise the Account Switcher with various options. It returns state and actions that can be taken on the Account Switcher.
+
+---
+
+## Usage Guide
+
+### API
+
+#### Installation
+
+```bash
+yarn add @twilio-paste/account-switcher - or - yarn add @twilio-paste/account-switcher
+```
+
+#### Usage
+
+```jsx
+import {
+ AccountSwitcher,
+ AccountSwitcherBadge,
+ AccountSwitcherGroup,
+ AccountSwitcherItem,
+ AccountSwitcherItemRadio,
+ AccountSwitcherSeparator,
+ useAccountSwitcherState,
+} from '@twilio-paste/core/account-switcher';
+
+const AccountSwitcherMenu = () => {
+ const accountSwitcher = useAccountSwitcherState();
+ const [selectedAccount, setSelectedAccount] = React.useState('Owl Telehealth');
+ return (
+ <>
+
+ Owl Telehealth
+
+
+
+ setSelectedAccount('Owl Telehealth')}
+ {...accountSwitcher}
+ >
+ Owl Telehealth
+
+ setSelectedAccount('Owl Health Demo')}
+ {...accountSwitcher}
+ >
+ Owl Health Demo
+
+ setSelectedAccount('Owl Subway')}
+ {...accountSwitcher}
+ >
+ Owl Subway
+
+
+
+
+ Account settings
+
+
+
+ View all accounts
+
+
+ View all subaccounts
+
+
+
+ Admin Center
+
+
+ >
+ );
+};
+```
+
+#### Props
+
+##### useAccountSwitcherState
+
+Pass these as part of an object to the `useAccountSwitcherState` hook.
+
+| Prop | Type | Description | Default |
+| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | ------- |
+| baseId | `string` | ID that will serve as a base for all the items IDs. | |
+| rtl | `boolean` | | |
+| orientation | `horizontal, vertical, undefined` | | |
+| currentId | `string, null, undefined` | The current focused item `id`. | |
+| loop | `boolean, horizontal, vertical` | | |
+| wrap | `boolean, horizontal, vertical` | | |
+| visible | `boolean` | Whether it's visible or not. | |
+| animated | `number, boolean` | | |
+| placement | `auto-start, auto, auto-end, top-start, top, top-end, right-start, right, right-end, bottom-end, bottom, bottom-start, left-end, left, left-start` | | |
+| gutter | `number, undefined` | Offset between the reference and the popover on the main axis. Should not be combined with `unstable_offset`. | |
+
+##### useAccountSwitcherState returned props
+
+These props are returned by the state hook. You can spread them into this component (`{...state}`) or pass them separately. You can also provide these props from your own state logic.
+
+| Prop | Type | Description | Default |
+| ------------- | --------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
+| baseId | `string` | ID that will serve as a base for all the items IDs. | |
+| baseId | string | ID that will serve as a base for all the items IDs. | |
+| visible | boolean | Whether it's visible or not. | |
+| animated | number | boolean | | |
+| modal | boolean | Toggles Dialog's `modal` state. | |
+| animating | boolean | Whether it's animating or not. | |
+| stopAnimation | () => void | Stops animation. It's called automatically if there's a CSS transition. | |
+| hide | () => void | Changes the `visible` state to `false` | |
+| placement | "auto-start" | "auto" | "auto-end" | "top-start... | Actual `placement`. | |
+| orientation | "horizontal" | "vertical" | undefined | Defines the orientation of the composite widget. | |
+| currentId | string | null | undefined | The current focused item `id`. | |
+| wrap | boolean | "horizontal" | "vertical" | If enabled, moving to the next item from the last one in a row or column will focus the first item in the next row or column and vice-versa. | |
+| groups | Group[] | Lists all the composite groups with their `id` and DOM `ref`. | |
+| items | Item[] | Lists all the composite items with their `id`, DOM `ref`, `disabled` state and `groupId` if any. | |
+| setCurrentId | (value: SetStateAction<string | null | undefine... | Sets `currentId`. | |
+| first | () => void | Moves focus to the first item. | |
+| last | () => void | Moves focus to the last item. | |
+| move | (id: string | null) => void | Moves focus to a given item ID. | |
+| next | (unstable_allTheWay?: boolean | undefined) => void | Moves focus to the next item. | |
+| previous | (unstable_allTheWay?: boolean | undefined) => void | Moves focus to the previous item. | |
+
+##### AccountSwitcher
+
+| Prop | Type | Description | Default |
+| ------------------ | -------------------- | ----------------------------------------------------------------------------------------- | ------------------ |
+| hideOnClickOutside | `boolean, undefined` | When enabled, user can hide the dialog by clicking outside it. | |
+| disabled | `boolean, undefined` | Same as the HTML attribute. | |
+| element? | `string` | Overrides the default element name to apply unique styles with the Customization Provider | 'ACCOUNT_SWITCHER' |
+
+##### AccountSwitcherBadge
+
+| Prop | Type | Description | Default |
+| --------------- | ---------------------------------------- | -------------------------------------------------------------------------------------------- | ------------------------ |
+| as? | `string` | The HTML tag to replace the default `
+
+
diff --git a/packages/paste-website/src/pages/components/multiselect-combobox/index.mdx b/packages/paste-website/src/pages/components/multiselect-combobox/index.mdx
index 2fc2cec4b6..41fc08098c 100644
--- a/packages/paste-website/src/pages/components/multiselect-combobox/index.mdx
+++ b/packages/paste-website/src/pages/components/multiselect-combobox/index.mdx
@@ -1,5 +1,5 @@
export const meta = {
- title: 'MultiselectCombobox',
+ title: 'Multiselect Combobox',
package: '@twilio-paste/combobox',
description:
'A multiselect combobox is a styled dropdown form element that allows users to select multiple values from a list.',
diff --git a/packages/paste-website/src/pages/components/product-switcher/index.mdx b/packages/paste-website/src/pages/components/product-switcher/index.mdx
new file mode 100644
index 0000000000..12df21a019
--- /dev/null
+++ b/packages/paste-website/src/pages/components/product-switcher/index.mdx
@@ -0,0 +1,310 @@
+export const meta = {
+ title: 'Product Switcher - Components',
+ package: '@twilio-paste/product-switcher',
+ description:
+ 'A Product Switcher is an opinionated Menu that presents a list of Twilio products that a user has access to and can switch between.',
+ slug: '/components/product-switcher/',
+};
+
+import Changelog from '@twilio-paste/product-switcher/CHANGELOG.md';
+import {StatusBadge} from '@twilio-paste/product-switcher';
+
+import {Box} from '@twilio-paste/box';
+import packageJson from '@twilio-paste/product-switcher/package.json';
+import {
+ ProductSwitcher,
+ ProductSwitcherButton,
+ ProductSwitcherItem,
+ useProductSwitcherState,
+} from '@twilio-paste/product-switcher';
+import {LogoTwilioIcon} from '@twilio-paste/icons/esm/LogoTwilioIcon';
+import {ProductSegmentIcon} from '@twilio-paste/icons/esm/ProductSegmentIcon';
+import {ProductFlexIcon} from '@twilio-paste/icons/esm/ProductFlexIcon';
+import {ProductEmailAPIIcon} from '@twilio-paste/icons/esm/ProductEmailAPIIcon';
+
+import {SidebarCategoryRoutes} from '../../../constants';
+import DefaultLayout from '../../../layouts/DefaultLayout';
+import {getFeature, getNavigationData} from '../../../utils/api';
+import {productSwitcherExample} from '../../../component-examples/ProductSwitcherExamples';
+
+export default DefaultLayout;
+
+export const getStaticProps = async () => {
+ const navigationData = await getNavigationData();
+ const feature = await getFeature('Product Switcher');
+ return {
+ props: {
+ data: {
+ ...packageJson,
+ ...feature,
+ },
+ navigationData,
+ },
+ };
+};
+
+
+
+---
+
+
+
+
+
+
+
+
+ {productSwitcherExample}
+
+
+## Guidelines
+
+## About Product Switcher
+
+Product Switcher is an implementation of [Menu](/components/menu) that shows the Twilio products a user has access to and can switch between.
+
+It's a mechanism to jump between top-level Twilio product applications such as Twilio Console, Segment, SendGrid, and Flex. It is ideally placed in the application [Topbar](/components/topbar).
+
+### Accessibility
+
+The Product Switcher implements a Menu and supports all the same keyboard navigation that the [Menu](/components/menu) component does.
+
+---
+
+## Composition Notes
+
+The Product Switcher is made up of the following sub-components:
+
+- `ProductSwitcherButton`
+- `ProductSwitcher`
+- `ProductSwitcherItem`
+- `useProductSwitcherState`
+
+### Product Switcher Button
+
+Product Switcher Button is a composed [icon-only Menu Button](/components/menu#choosing-a-menu-trigger).
+
+### Product Switcher
+
+Container for Product Switcher menu items.
+
+### Product Switcher Item
+
+A radio menu item that can either perform an action or navigate to a new url. It can show the currently selected product the user is in. Only one product can be selected at a time via the `checked` property. Ensure that the Items all belong to the same group by sharing a `name`.
+
+### useProductSwitcherState hook
+
+React Hook used to initialise the Product Switcher with various options. It returns state and actions that can be taken on the Product Switcher.
+
+---
+
+## Usage Guide
+
+### API
+
+#### Installation
+
+```bash
+yarn add @twilio-paste/product-switcher - or - yarn add @twilio-paste/product-switcher
+```
+
+#### Usage
+
+```jsx
+import {
+ ProductSwitcher,
+ ProductSwitcherButton,
+ ProductSwitcherItem,
+ useProductSwitcherState,
+} from '@twilio-paste/core/product-switcher';
+
+const ProductSwitcherMenu = () => {
+ const productSwitcher = useProductSwitcherState();
+ const [product, setProduct] = React.useState('twilio');
+ return (
+ <>
+
+
+ {
+ setProduct('twilio');
+ }}
+ productName="Twilio"
+ productStrapline="SMS, Voice & Video"
+ productIcon={}
+ />
+ {
+ setProduct('segment');
+ }}
+ productName="Segment"
+ productStrapline="Customer data platform"
+ productIcon={}
+ />
+ {
+ setProduct('flex');
+ }}
+ productName="Flex"
+ productStrapline="Cloud-based contact center"
+ productIcon={}
+ />
+ {
+ setProduct('sendgrid');
+ }}
+ productName="SendGrid"
+ productStrapline="Email delivery and API"
+ productIcon={}
+ />
+ {
+ setProduct('admin');
+ }}
+ productName="Console Admin"
+ productStrapline="Admin center"
+ productIcon={}
+ />
+
+ >
+ );
+};
+```
+
+#### Props
+
+##### useProductSwitcher
+
+Pass these as part of an object to the `useProductSwitcher` hook.
+
+| Prop | Type | Description | Default |
+| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | ------- |
+| baseId | `string` | ID that will serve as a base for all the items IDs. | |
+| rtl | `boolean` | | |
+| orientation | `horizontal, vertical, undefined` | | |
+| currentId | `string, null, undefined` | The current focused item `id`. | |
+| loop | `boolean, horizontal, vertical` | | |
+| wrap | `boolean, horizontal, vertical` | | |
+| visible | `boolean` | Whether it's visible or not. | |
+| animated | `number, boolean` | | |
+| placement | `auto-start, auto, auto-end, top-start, top, top-end, right-start, right, right-end, bottom-end, bottom, bottom-start, left-end, left, left-start` | | |
+| gutter | `number, undefined` | Offset between the reference and the popover on the main axis. Should not be combined with `unstable_offset`. | |
+
+##### useProductSwitcher returned props
+
+These props are returned by the state hook. You can spread them into this component (`{...state}`) or pass them separately. You can also provide these props from your own state logic.
+
+| Prop | Type | Description | Default |
+| ------------- | --------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
+| baseId | `string` | ID that will serve as a base for all the items IDs. | |
+| baseId | string | ID that will serve as a base for all the items IDs. | |
+| visible | boolean | Whether it's visible or not. | |
+| animated | number | boolean | | |
+| modal | boolean | Toggles Dialog's `modal` state. | |
+| animating | boolean | Whether it's animating or not. | |
+| stopAnimation | () => void | Stops animation. It's called automatically if there's a CSS transition. | |
+| hide | () => void | Changes the `visible` state to `false` | |
+| placement | "auto-start" | "auto" | "auto-end" | "top-start... | Actual `placement`. | |
+| orientation | "horizontal" | "vertical" | undefined | Defines the orientation of the composite widget. | |
+| currentId | string | null | undefined | The current focused item `id`. | |
+| wrap | boolean | "horizontal" | "vertical" | If enabled, moving to the next item from the last one in a row or column will focus the first item in the next row or column and vice-versa. | |
+| groups | Group[] | Lists all the composite groups with their `id` and DOM `ref`. | |
+| items | Item[] | Lists all the composite items with their `id`, DOM `ref`, `disabled` state and `groupId` if any. | |
+| setCurrentId | (value: SetStateAction<string | null | undefine... | Sets `currentId`. | |
+| first | () => void | Moves focus to the first item. | |
+| last | () => void | Moves focus to the last item. | |
+| move | (id: string | null) => void | Moves focus to a given item ID. | |
+| next | (unstable_allTheWay?: boolean | undefined) => void | Moves focus to the next item. | |
+| previous | (unstable_allTheWay?: boolean | undefined) => void | Moves focus to the previous item. | |
+
+##### ProductSwitcher
+
+| Prop | Type | Description | Default |
+| ------------------ | -------------------- | ----------------------------------------------------------------------------------------- | ------------------ |
+| hideOnClickOutside | `boolean, undefined` | When enabled, user can hide the dialog by clicking outside it. | |
+| disabled | `boolean, undefined` | Same as the HTML attribute. | |
+| element? | `string` | Overrides the default element name to apply unique styles with the Customization Provider | 'PRODUCT_SWITCHER' |
+
+##### ProductSwitcherButton
+
+| Prop | Type | Description | Default |
+| ---------------------- | ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | -------------------------------------- |
+| as? | `string` | The HTML tag to replace the default `
+
+
diff --git a/packages/paste-website/src/pages/components/status-badge/index.mdx b/packages/paste-website/src/pages/components/status-badge/index.mdx
index 96daa10227..4e07232246 100644
--- a/packages/paste-website/src/pages/components/status-badge/index.mdx
+++ b/packages/paste-website/src/pages/components/status-badge/index.mdx
@@ -1,8 +1,7 @@
export const meta = {
title: 'Status Badge - Components',
package: '@twilio-paste/status',
- description:
- 'A status should be used to communicate to a user the status of a process or connection to an application or service.',
+ description: 'A Status Badge communicates the status of a process or connectivity to an application or service.',
slug: '/components/status-badge/',
};
@@ -36,6 +35,7 @@ export const getStaticProps = async () => {
categoryRoute={SidebarCategoryRoutes.COMPONENTS}
githubUrl="https://github.com/twilio-labs/paste/tree/main/packages/paste-core/components/status"
storybookUrl="/?path=/story/components-status--"
+ name="Status Badge"
data={props.data}
/>
@@ -63,7 +63,7 @@ export const getStaticProps = async () => {
Neutral
- In Progress
+ In-progress
Disabled
@@ -97,8 +97,14 @@ export const getStaticProps = async () => {
## About Status Badge
+Status badge is an implementation of the [Status Pattern](/patterns/status) to display the status of a process or connectivity in your product.
+
### Accessibility
+Status Badge communicates a status in an accessible way by automatically pairing an icon with a color that represents a specific status type. This ensures there are distinct visual differences between each status type.
+
+Care must still be taken to ensure the word used to describe the status in the badge is unique to the status type it is paired with for any given screen.
+
## Examples
### Variants
@@ -143,7 +149,7 @@ export const getStaticProps = async () => {
{`
- In Progress
+ In-progress
`.trim()}
@@ -231,7 +237,7 @@ Use small Badges sparingly, only when needed for vertical density. Guidelines fo
Neutral
- In Progress
+ In-progress
Disabled
@@ -267,11 +273,7 @@ Use small Badges sparingly, only when needed for vertical density. Guidelines fo
Use a Status Badge when you want to add a label to a piece of UI for quick identification and navigation.
-Status Badge labels should be one to three words, or numbers.
-
-### Use cases
-
-### Do & Don't
+Status Badge labels should be 1–3 words.
---
diff --git a/packages/paste-website/src/pages/components/status-menu/index.mdx b/packages/paste-website/src/pages/components/status-menu/index.mdx
index 7e3fa99559..c783504dd1 100644
--- a/packages/paste-website/src/pages/components/status-menu/index.mdx
+++ b/packages/paste-website/src/pages/components/status-menu/index.mdx
@@ -2,7 +2,7 @@ export const meta = {
title: 'Status Menu - Components',
package: '@twilio-paste/status',
description:
- 'A status should be used to communicate to a user the status of a process or connection to an application or service.',
+ 'A Status Menu communicates the status of a process or connectivity to an application or service, and provides a way to update that status type.',
slug: '/components/status-menu/',
};
@@ -15,34 +15,17 @@ import {
StatusMenuItemRadio,
StatusMenuItemCheckbox,
} from '@twilio-paste/status';
-import Changelog from '@twilio-paste/menu/CHANGELOG.md';
-import {Box} from '@twilio-paste/box';
-import {Text} from '@twilio-paste/text';
-import {Paragraph} from '@twilio-paste/paragraph';
-import {MediaObject, MediaBody, MediaFigure} from '@twilio-paste/media-object';
-import {AttachIcon} from '@twilio-paste/icons/esm/AttachIcon';
-import {ChevronDownIcon} from '@twilio-paste/icons/esm/ChevronDownIcon';
-import {UnorderedListIcon} from '@twilio-paste/icons/esm/UnorderedListIcon';
-import {DataBarChartIcon} from '@twilio-paste/icons/esm/DataBarChartIcon';
-import {DataLineChartIcon} from '@twilio-paste/icons/esm/DataLineChartIcon';
-import {DataPieChartIcon} from '@twilio-paste/icons/esm/DataPieChartIcon';
-import {DataTableIcon} from '@twilio-paste/icons/esm/DataTableIcon';
-import {BoldIcon} from '@twilio-paste/icons/esm/BoldIcon';
-import {UnderlineIcon} from '@twilio-paste/icons/esm/UnderlineIcon';
-import {ItalicIcon} from '@twilio-paste/icons/esm/ItalicIcon';
-import {StrikethroughIcon} from '@twilio-paste/icons/esm/StrikethroughIcon';
-import {MoreIcon} from '@twilio-paste/icons/esm/MoreIcon';
-import {Callout, CalloutHeading, CalloutText} from '@twilio-paste/callout';
+import Changelog from '@twilio-paste/status/CHANGELOG.md';
+import packageJson from '@twilio-paste/status/package.json';
+
import {SidebarCategoryRoutes} from '../../../constants';
import {DoDont, Do, Dont} from '../../../components/DoDont';
-import {InlineCode} from '../../../components/Typography';
import {
processStatusMenuExample,
availabilityStatusMenuExample,
ConnectivityObject,
ProcessObject,
-} from '../../../component-examples/StatusMenuExamples.ts';
-import packageJson from '@twilio-paste/status/package.json';
+} from '../../../component-examples/StatusMenuExamples';
import DefaultLayout from '../../../layouts/DefaultLayout';
import {getFeature, getNavigationData} from '../../../utils/api';
@@ -65,7 +48,8 @@ export const getStaticProps = async () => {
@@ -81,7 +65,7 @@ export const getStaticProps = async () => {
scope={{
useStatusMenuState,
StatusMenu,
- StatusMenuItem,
+ StatusMenuItemRadio,
StatusMenuBadge,
StatusMenuItemChild,
ProcessObject,
@@ -94,23 +78,25 @@ export const getStaticProps = async () => {
## Guidelines
-### About Menu
+### About Status Menu
+
+Use a Status Menu to both display and change the status of a connection or process. It closely follows the [Status Pattern](/patterns/status) and comes with preconfigured options for displaying the status of both processes and connections, using the correct icons for each.
### Accessibility
-## Examples
+Status Menu implements a Menu and supports all the same keyboard navigation that the [Menu](/components/menu) component does.
-### Basic menu
+## Examples
-Status menus follow the [Process and Connectivity Status Patterns](/patterns/status). They can be used to display and update Connectivity and Process statuses.
+### Process menu
-#### Process
+Use the process-related variants of Status Menu to display and interact with the status of a particular process.
-#### Connectivity
+### Connectivity Menu
+
+Use the connectivity-related variants of Status Menu to display and interact with the status of a particular connection.
{
+const ProcessStatusMenu = () => {
+ const [process, setProcess] = React.useState(ProcessObject.Success);
const menu = useStatusMenuState();
+ const onClick = (status) => {
+ setProcess(ProcessObject[status]);
+ menu.hide();
+ };
return (
<>
-
- Preferences
+
+ {process.children}
- Settings
-
- Extensions
+ onClick('Success')} variant="default">
+ Complete
+
+ onClick('Neutral')} variant="default">
+ In review
+
+ onClick('Warning')} variant="default">
+ Needs attention
+
+ onClick('Error')} variant="default">
+ Rejected
+
+ onClick('InProgress')} variant="default">
+ In-progress
+
+ onClick('Disabled')} variant="default">
+ Paused
+
+ onClick('Draft')} variant="default">
+ Draft
- Keyboard shortcuts
-
+
>
);
};
diff --git a/packages/paste-website/src/pages/introduction/contributing/components/index.mdx b/packages/paste-website/src/pages/introduction/contributing/components/index.mdx
index 548a8b422c..3f169a368a 100644
--- a/packages/paste-website/src/pages/introduction/contributing/components/index.mdx
+++ b/packages/paste-website/src/pages/introduction/contributing/components/index.mdx
@@ -79,7 +79,7 @@ This is the process we follow when creating a net-new component for Paste. This
## Where do I start?
-Check out our [roadmap](/roadmap) to see what's available. Anything that’s not “complete” or “in progress” is up for grabs.
+Check out our [roadmap](/roadmap) to see what's available. Anything that’s not “complete” or “in-progress” is up for grabs.
Have a new idea? [Create a Github Discussion](https://github.com/twilio-labs/paste/discussions/new) to see if there’s
a need for something else that’s not already on the roadmap.
diff --git a/packages/paste-website/src/pages/patterns/status/index.mdx b/packages/paste-website/src/pages/patterns/status/index.mdx
index 326e3ccfda..23038afcc4 100644
--- a/packages/paste-website/src/pages/patterns/status/index.mdx
+++ b/packages/paste-website/src/pages/patterns/status/index.mdx
@@ -400,7 +400,7 @@ to ensure that the icon and the text are vertically aligned.
### Status badges
-For convience and consistency, we have created a `StatusBadge` (coming soon) component that can be used to compose a status icon with a text label.
+Use the [`StatusBadge`](/components/status-badge) component to compose a status icon with a text label, usually when a status is shown outside of a Table. Common placements are near avatars (particularly for connectivity statuses), in cards, and in lists.
{`
@@ -418,7 +418,7 @@ For convience and consistency, we have created a `StatusBadge` (coming soon) com
Neutral
- In Progress
+ In-progress
Disabled
@@ -585,8 +585,8 @@ Statuses are not always placed in tables. Other common placements are near avata
-
- In Progress
+
+ In-progress
@@ -643,8 +643,8 @@ Statuses are not always placed in tables. Other common placements are near avata