From 7ee9772916b62c5bde305ac6492255ac7355c46d Mon Sep 17 00:00:00 2001
From: krisantrobus <55083528+krisantrobus@users.noreply.github.com>
Date: Thu, 18 Jul 2024 14:14:14 -0500
Subject: [PATCH] feat(badge): add notification variant (#3993)
* feat(badge): notifications counter wip
* chore(badge): update styling
* feat(badge): use new design tokens for notification
* feat(badge): changeset
* feat(badge): typeocs
* feat(badge): lint for variant naming
* feat(docs/badge): add variant to docs page
* feat(docs/badge): merged counter and notification counter
* feat(docs/badge): added notifications in first example
---
.changeset/smooth-gorillas-care.md | 6 +
.eslintrc.js | 1 +
.../components/badge/src/constants.ts | 1 +
.../paste-core/components/badge/src/styles.ts | 9 ++
.../badge/stories/index.stories.tsx | 55 +++++++
.../components/badge/type-docs.json | 4 +-
.../paste-core/components/menu/type-docs.json | 2 +-
.../components/popover/type-docs.json | 2 +-
.../components/side-panel/type-docs.json | 2 +-
.../src/pages/components/badge/index.mdx | 141 ++++++++++--------
10 files changed, 152 insertions(+), 71 deletions(-)
create mode 100644 .changeset/smooth-gorillas-care.md
diff --git a/.changeset/smooth-gorillas-care.md b/.changeset/smooth-gorillas-care.md
new file mode 100644
index 0000000000..b0bf2d139d
--- /dev/null
+++ b/.changeset/smooth-gorillas-care.md
@@ -0,0 +1,6 @@
+---
+"@twilio-paste/badge": minor
+"@twilio-paste/core": minor
+---
+
+[Badge] Added a new notification variant
diff --git a/.eslintrc.js b/.eslintrc.js
index 611d6edb92..77fbf08f1a 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -87,6 +87,7 @@ module.exports = {
"icon_small",
"error_counter",
"neutral_counter",
+ "notification_counter",
// unstable props are allowed
"^unstable_",
// this is a temporary prop, if the console patch is removed from components this can be removed too
diff --git a/packages/paste-core/components/badge/src/constants.ts b/packages/paste-core/components/badge/src/constants.ts
index 33f6678e8e..58f514df98 100644
--- a/packages/paste-core/components/badge/src/constants.ts
+++ b/packages/paste-core/components/badge/src/constants.ts
@@ -15,6 +15,7 @@ export const BadgeVariants = [
"neutral_counter",
"error_counter",
"default",
+ "notification_counter",
// the following variants are outdated but still supported to prevent breaking changes
"info",
] as const;
diff --git a/packages/paste-core/components/badge/src/styles.ts b/packages/paste-core/components/badge/src/styles.ts
index b6b86cce76..a09d975e3d 100644
--- a/packages/paste-core/components/badge/src/styles.ts
+++ b/packages/paste-core/components/badge/src/styles.ts
@@ -110,6 +110,12 @@ export const badgeVariantStyles: {
color: "colorText",
boxShadow: "shadowBorderWeaker",
},
+ notification_counter: {
+ borderRadius: "borderRadiusPill",
+ backgroundColor: "colorBackgroundNotification",
+ color: "colorTextInverse",
+ boxShadow: "shadowBorderNotification",
+ },
/*
* the following variants are outdated but still supported to prevent breaking changes
*/
@@ -170,6 +176,9 @@ export const badgeButtonStyles: {
error_counter: {
boxShadow: "shadowBorderBottomErrorWeaker",
},
+ notification_counter: {
+ boxShadow: "shadowBorderBottomNotificationStronger",
+ },
// the following variants are outdated but still supported to prevent breaking changes
default: {
boxShadow: "shadowBorderBottomDecorative10Weaker",
diff --git a/packages/paste-core/components/badge/stories/index.stories.tsx b/packages/paste-core/components/badge/stories/index.stories.tsx
index f80b8b01a2..c7c9d7e0f1 100644
--- a/packages/paste-core/components/badge/stories/index.stories.tsx
+++ b/packages/paste-core/components/badge/stories/index.stories.tsx
@@ -77,6 +77,9 @@ export const AllBadges = (): JSX.Element => (
1
+
+ 100
+
);
export const SmallBadges = (): JSX.Element => (
@@ -126,6 +129,9 @@ export const SmallBadges = (): JSX.Element => (
1
+
+ 100
+
);
@@ -1031,3 +1037,52 @@ export const LongTextBadge = (): JSX.Element => (
);
+
+export const NotificationCounterBadge = (): JSX.Element => (
+ <>
+
+ Span
+
+
+
+ 100
+
+
+ 100
+
+
+ 100
+
+
+
+ Anchor
+
+
+
+ 100
+
+
+ 100
+
+
+ 100
+
+
+
+ Button
+
+
+ {}} variant="notification_counter">
+ 100
+
+ {}} variant="notification_counter">
+ 100
+
+ {}} variant="notification_counter">
+ 100
+
+
+ >
+);
+
+NotificationCounterBadge.storyName = "Notification Counter Badge";
diff --git a/packages/paste-core/components/badge/type-docs.json b/packages/paste-core/components/badge/type-docs.json
index 536d21d30a..1db543877b 100644
--- a/packages/paste-core/components/badge/type-docs.json
+++ b/packages/paste-core/components/badge/type-docs.json
@@ -1,7 +1,7 @@
{
"BadgeBase": {
"variant": {
- "type": "| \"neutral\"\n | \"warning\"\n | \"error\"\n | \"success\"\n | \"new\"\n | \"subaccount\"\n | \"decorative10\"\n | \"decorative20\"\n | \"decorative30\"\n | \"decorative40\"\n | \"brand10\"\n | \"brand20\"\n | \"brand30\"\n | \"neutral_counter\"\n | \"error_counter\"\n | \"default\"\n | \"info\"",
+ "type": "| \"neutral\"\n | \"warning\"\n | \"error\"\n | \"success\"\n | \"new\"\n | \"subaccount\"\n | \"decorative10\"\n | \"decorative20\"\n | \"decorative30\"\n | \"decorative40\"\n | \"brand10\"\n | \"brand20\"\n | \"brand30\"\n | \"neutral_counter\"\n | \"error_counter\"\n | \"default\"\n | \"notification_counter\"\n | \"info\"",
"defaultValue": "null",
"required": true,
"externalProp": false
@@ -29,7 +29,7 @@
"description": "Underlying HTML element to render. Can be \"span\", \"button\", or \"a\"."
},
"variant": {
- "type": "| \"neutral\"\n | \"warning\"\n | \"error\"\n | \"success\"\n | \"new\"\n | \"subaccount\"\n | \"decorative10\"\n | \"decorative20\"\n | \"decorative30\"\n | \"decorative40\"\n | \"brand10\"\n | \"brand20\"\n | \"brand30\"\n | \"neutral_counter\"\n | \"error_counter\"\n | \"default\"\n | \"info\"",
+ "type": "| \"neutral\"\n | \"warning\"\n | \"error\"\n | \"success\"\n | \"new\"\n | \"subaccount\"\n | \"decorative10\"\n | \"decorative20\"\n | \"decorative30\"\n | \"decorative40\"\n | \"brand10\"\n | \"brand20\"\n | \"brand30\"\n | \"neutral_counter\"\n | \"error_counter\"\n | \"default\"\n | \"notification_counter\"\n | \"info\"",
"defaultValue": "null",
"required": true,
"externalProp": false
diff --git a/packages/paste-core/components/menu/type-docs.json b/packages/paste-core/components/menu/type-docs.json
index c8d2544b21..bc8d00ce7a 100644
--- a/packages/paste-core/components/menu/type-docs.json
+++ b/packages/paste-core/components/menu/type-docs.json
@@ -2477,7 +2477,7 @@
"description": "Toggles the `visible` state"
},
"variant": {
- "type": "| \"default\"\n | \"neutral\"\n | \"warning\"\n | \"error\"\n | \"success\"\n | \"new\"\n | \"subaccount\"\n | \"decorative10\"\n | \"decorative20\"\n | \"decorative30\"\n | \"decorative40\"\n | \"brand10\"\n | \"brand20\"\n | \"brand30\"\n | \"neutral_counter\"\n | \"error_counter\"\n | \"info\"",
+ "type": "| \"default\"\n | \"neutral\"\n | \"warning\"\n | \"error\"\n | \"success\"\n | \"new\"\n | \"subaccount\"\n | \"decorative10\"\n | \"decorative20\"\n | \"decorative30\"\n | \"decorative40\"\n | \"brand10\"\n | \"brand20\"\n | \"brand30\"\n | \"neutral_counter\"\n | \"error_counter\"\n | \"notification_counter\"\n | \"info\"",
"defaultValue": "null",
"required": true,
"externalProp": false
diff --git a/packages/paste-core/components/popover/type-docs.json b/packages/paste-core/components/popover/type-docs.json
index f34295b6f6..3d84791579 100644
--- a/packages/paste-core/components/popover/type-docs.json
+++ b/packages/paste-core/components/popover/type-docs.json
@@ -3560,7 +3560,7 @@
},
"PopoverBadgeButton": {
"variant": {
- "type": "| \"default\"\n | \"neutral\"\n | \"warning\"\n | \"error\"\n | \"success\"\n | \"new\"\n | \"subaccount\"\n | \"decorative10\"\n | \"decorative20\"\n | \"decorative30\"\n | \"decorative40\"\n | \"brand10\"\n | \"brand20\"\n | \"brand30\"\n | \"neutral_counter\"\n | \"error_counter\"\n | \"info\"",
+ "type": "| \"default\"\n | \"neutral\"\n | \"warning\"\n | \"error\"\n | \"success\"\n | \"new\"\n | \"subaccount\"\n | \"decorative10\"\n | \"decorative20\"\n | \"decorative30\"\n | \"decorative40\"\n | \"brand10\"\n | \"brand20\"\n | \"brand30\"\n | \"neutral_counter\"\n | \"error_counter\"\n | \"notification_counter\"\n | \"info\"",
"defaultValue": "null",
"required": true,
"externalProp": false
diff --git a/packages/paste-core/components/side-panel/type-docs.json b/packages/paste-core/components/side-panel/type-docs.json
index 854fd87f72..e072bdef9b 100644
--- a/packages/paste-core/components/side-panel/type-docs.json
+++ b/packages/paste-core/components/side-panel/type-docs.json
@@ -1712,7 +1712,7 @@
},
"SidePanelBadgeButton": {
"variant": {
- "type": "| \"default\"\n | \"neutral\"\n | \"warning\"\n | \"error\"\n | \"success\"\n | \"new\"\n | \"subaccount\"\n | \"decorative10\"\n | \"decorative20\"\n | \"decorative30\"\n | \"decorative40\"\n | \"brand10\"\n | \"brand20\"\n | \"brand30\"\n | \"neutral_counter\"\n | \"error_counter\"\n | \"info\"",
+ "type": "| \"default\"\n | \"neutral\"\n | \"warning\"\n | \"error\"\n | \"success\"\n | \"new\"\n | \"subaccount\"\n | \"decorative10\"\n | \"decorative20\"\n | \"decorative30\"\n | \"decorative40\"\n | \"brand10\"\n | \"brand20\"\n | \"brand30\"\n | \"neutral_counter\"\n | \"error_counter\"\n | \"notification_counter\"\n | \"info\"",
"defaultValue": "null",
"required": true,
"externalProp": false
diff --git a/packages/paste-website/src/pages/components/badge/index.mdx b/packages/paste-website/src/pages/components/badge/index.mdx
index 98ce1d893d..314be3f9d1 100644
--- a/packages/paste-website/src/pages/components/badge/index.mdx
+++ b/packages/paste-website/src/pages/components/badge/index.mdx
@@ -1,68 +1,68 @@
export const meta = {
- title: 'Badge - Components',
- package: '@twilio-paste/badge',
- description: 'A badge is a visually highlighted text label that describes an attribute of an object.',
- slug: '/components/badge/',
+ title: "Badge - Components",
+ package: "@twilio-paste/badge",
+ description: "A badge is a visually highlighted text label that describes an attribute of an object.",
+ slug: "/components/badge/",
};
-import Changelog from '@twilio-paste/badge/CHANGELOG.md';
-
-import {Badge} from '@twilio-paste/badge';
-import {Card} from '@twilio-paste/card';
-import {Text} from '@twilio-paste/text';
-import {Heading} from '@twilio-paste/heading';
-import {Button} from '@twilio-paste/button';
-import {Modal, ModalBody, ModalFooter, ModalFooterActions, ModalHeader, ModalHeading} from '@twilio-paste/modal';
-import {Radio, RadioGroup} from '@twilio-paste/radio-group';
-import {InformationIcon} from '@twilio-paste/icons/esm/InformationIcon';
-import {WarningIcon} from '@twilio-paste/icons/esm/WarningIcon';
-import {SuccessIcon} from '@twilio-paste/icons/esm/SuccessIcon';
-import {ErrorIcon} from '@twilio-paste/icons/esm/ErrorIcon';
-import {NewIcon} from '@twilio-paste/icons/esm/NewIcon';
-import {UsersIcon} from '@twilio-paste/icons/esm/UsersIcon';
-import {ProductVoiceIcon} from '@twilio-paste/icons/esm/ProductVoiceIcon';
-import {ProductElasticSIPTrunkingIcon} from '@twilio-paste/icons/esm/ProductElasticSIPTrunkingIcon';
-import {SMSCapableIcon} from '@twilio-paste/icons/esm/SMSCapableIcon';
-import {MMSCapableIcon} from '@twilio-paste/icons/esm/MMSCapableIcon';
-import {VoiceCapableIcon} from '@twilio-paste/icons/esm/VoiceCapableIcon';
-import {FaxCapableIcon} from '@twilio-paste/icons/esm/FaxCapableIcon';
-import {LinkExternalIcon} from '@twilio-paste/icons/esm/LinkExternalIcon';
+import Changelog from "@twilio-paste/badge/CHANGELOG.md";
+
+import { Badge } from "@twilio-paste/badge";
+import { Card } from "@twilio-paste/card";
+import { Text } from "@twilio-paste/text";
+import { Heading } from "@twilio-paste/heading";
+import { Button } from "@twilio-paste/button";
+import { Modal, ModalBody, ModalFooter, ModalFooterActions, ModalHeader, ModalHeading } from "@twilio-paste/modal";
+import { Radio, RadioGroup } from "@twilio-paste/radio-group";
+import { InformationIcon } from "@twilio-paste/icons/esm/InformationIcon";
+import { WarningIcon } from "@twilio-paste/icons/esm/WarningIcon";
+import { SuccessIcon } from "@twilio-paste/icons/esm/SuccessIcon";
+import { ErrorIcon } from "@twilio-paste/icons/esm/ErrorIcon";
+import { NewIcon } from "@twilio-paste/icons/esm/NewIcon";
+import { UsersIcon } from "@twilio-paste/icons/esm/UsersIcon";
+import { ProductVoiceIcon } from "@twilio-paste/icons/esm/ProductVoiceIcon";
+import { ProductElasticSIPTrunkingIcon } from "@twilio-paste/icons/esm/ProductElasticSIPTrunkingIcon";
+import { SMSCapableIcon } from "@twilio-paste/icons/esm/SMSCapableIcon";
+import { MMSCapableIcon } from "@twilio-paste/icons/esm/MMSCapableIcon";
+import { VoiceCapableIcon } from "@twilio-paste/icons/esm/VoiceCapableIcon";
+import { FaxCapableIcon } from "@twilio-paste/icons/esm/FaxCapableIcon";
+import { LinkExternalIcon } from "@twilio-paste/icons/esm/LinkExternalIcon";
import { ProductCommsIcon } from "@twilio-paste/icons/esm/ProductCommsIcon";
import { ProductSendGridIcon } from "@twilio-paste/icons/esm/ProductSendGridIcon";
import { ProductSegmentIcon } from "@twilio-paste/icons/esm/ProductSegmentIcon";
-import {Grid, Column} from '@twilio-paste/grid';
-import {Tabs, Tab, TabList, TabPanel, TabPanels} from '@twilio-paste/tabs';
-import {DisplayPill} from '@twilio-paste/display-pill-group';
-
-import {Truncate} from '@twilio-paste/truncate';
-import {styled, css} from '@twilio-paste/styling-library';
-
-import {Box} from '@twilio-paste/box';
-import {UnorderedList, ListItem} from '@twilio-paste/list';
-import {Stack} from '@twilio-paste/stack';
-import {Paragraph} from '@twilio-paste/paragraph';
-import {PopoverContainer, PopoverBadgeButton, Popover} from '@twilio-paste/popover';
-import {Table, THead, TBody, Td, Th, Tr} from '@twilio-paste/table';
-import {useUID} from '@twilio-paste/uid-library';
-
-import {SidebarCategoryRoutes} from '../../../constants';
-import {DoDont, Do, Dont} from '../../../components/DoDont';
+import { Grid, Column } from "@twilio-paste/grid";
+import { Tabs, Tab, TabList, TabPanel, TabPanels } from "@twilio-paste/tabs";
+import { DisplayPill } from "@twilio-paste/display-pill-group";
+
+import { Truncate } from "@twilio-paste/truncate";
+import { styled, css } from "@twilio-paste/styling-library";
+
+import { Box } from "@twilio-paste/box";
+import { UnorderedList, ListItem } from "@twilio-paste/list";
+import { Stack } from "@twilio-paste/stack";
+import { Paragraph } from "@twilio-paste/paragraph";
+import { PopoverContainer, PopoverBadgeButton, Popover } from "@twilio-paste/popover";
+import { Table, THead, TBody, Td, Th, Tr } from "@twilio-paste/table";
+import { useUID } from "@twilio-paste/uid-library";
+
+import { SidebarCategoryRoutes } from "../../../constants";
+import { DoDont, Do, Dont } from "../../../components/DoDont";
import {
tableExample,
betaFeatureExample,
settingsAndProducts,
badgeModalExample,
counterExample,
-} from '../../../component-examples/BadgeExamples';
-import packageJson from '@twilio-paste/badge/package.json';
-import ComponentPageLayout from '../../../layouts/ComponentPageLayout';
-import {getFeature, getNavigationData} from '../../../utils/api';
+} from "../../../component-examples/BadgeExamples";
+import packageJson from "@twilio-paste/badge/package.json";
+import ComponentPageLayout from "../../../layouts/ComponentPageLayout";
+import { getFeature, getNavigationData } from "../../../utils/api";
export default ComponentPageLayout;
export const getStaticProps = async () => {
const navigationData = await getNavigationData();
- const feature = await getFeature('Badge');
+ const feature = await getFeature("Badge");
return {
props: {
data: {
@@ -73,14 +73,14 @@ export const getStaticProps = async () => {
mdxHeadings,
pageHeaderData: {
categoryRoute: SidebarCategoryRoutes.COMPONENTS,
- githubUrl: 'https://github.com/twilio-labs/paste/tree/main/packages/paste-core/components/badge',
- storybookUrl: '/?path=/story/components-badge--all-badges',
+ githubUrl: "https://github.com/twilio-labs/paste/tree/main/packages/paste-core/components/badge",
+ storybookUrl: "/?path=/story/components-badge--all-badges",
},
},
};
};
-
+
{`
Neutral
@@ -127,6 +127,9 @@ export const getStaticProps = async () => {
1
+
+ 100
+
`.trim()}
@@ -160,7 +163,7 @@ should be prefixed before the text.
Use the neutral Badge to highlight neutral attributes of an object. An information icon is optional.
-
+
{`
Neutral
@@ -177,7 +180,7 @@ Use the neutral Badge to highlight neutral attributes of an object. An informati
Use the warning Badge to highlight attributes of an object that the user must be made aware of to avoid incurring an error. A warning icon is recommended.
-
+
{`
Warning
@@ -198,7 +201,7 @@ Do not use an error Badge unless there is additional guidance elsewhere on the p
For additional guidance on how to compose error messages, refer to the [error state pattern](/patterns/error-state).
-
+
{`
Error
@@ -216,7 +219,7 @@ For additional guidance on how to compose error messages, refer to the [error st
Use the success Badge to highlight attributes of an object that were completed or are considered to be in a good
state. A success icon is recommended.
-
+
{`
Success
@@ -233,7 +236,7 @@ state. A success icon is recommended.
Use the new Badge to highlight an object that is new, beta, pilot, or experimental. A new icon is recommended.
-
+
{`
New
@@ -251,7 +254,7 @@ Use the new Badge to highlight an object that is new, beta, pilot, or experiment
The subaccount Badge is reserved for specific use cases only. Consult with the Console team before implementing this variant.
A users icon is recommended.
-
+
{`
Subaccount
@@ -269,7 +272,7 @@ A users icon is recommended.
Use the decorative Badge to highlight attributes that do not have a strictly semantic meaning (like warning, error, or success)
but would benefit from the visual affordance of differently-colored Badges. Icons are optional, and should appear before the text.
-
+
{`
@@ -295,7 +298,10 @@ but would benefit from the visual affordance of differently-colored Badges. Icon
Use the brand Badge for cross-selling opportunities across platforms (e.g., Segment, SendGrid, Communications, Flex) and pricing plans. Use these sparingly.
-
+
{`
@@ -315,11 +321,11 @@ Use the brand Badge for cross-selling opportunities across platforms (e.g., Segm
### Counter
-Use the counter Badge should be used to visually highlight a count in a UI.
-For example, the number of pending invitations or the number of errors.
-Counter Badges are limited to Neutral and Error variants, and the error variant should always include an error icon.
+Use the counter Badge to visually highlight a count in a UI, like the number of pending invitations or the number of errors.
+
+Counter Badges have neutral, error, and notification variants. The error variant should always include an error icon. The notification variant informs users of new or unread items, such as messages, notifications, or updates.
-
+
{`
100
@@ -327,6 +333,9 @@ Counter Badges are limited to Neutral and Error variants, and the error variant
100
+
+ 100
+
`.trim()}
@@ -336,7 +345,7 @@ Counter Badges are limited to Neutral and Error variants, and the error variant
A badge can link to other pages.
To do so, add an `href` prop and set `as="a"` on the Badge.
-
+
{`
@@ -381,7 +390,7 @@ of the Popover docs to add a Popover to a Badge.
Use a small Badge only when vertical density is a concern. The guidelines for using variants in a small Badge are the same as in their default size.
-
+
{`
Neutral
@@ -663,7 +672,7 @@ yarn add @twilio-paste/badge - or - yarn add @twilio-paste/core
#### Usage
```jsx
-import {Badge} from '@twilio-paste/core/badge';
+import { Badge } from "@twilio-paste/core/badge";
const BadgeExample = () => (