Skip to content

Commit

Permalink
Add support for an icon in the Banner component (#2249)
Browse files Browse the repository at this point in the history
## Summary:
Adds support for an icon (phosphor or custom) to the Banner component. This overrides the default icon used to represent the kind of Banner

Issue: WB-1447

## Test plan:
- Review Banner documentation (`?path=/docs/packages-banner--docs`)
  - new `icon` prop documentation
  - the example story descriptions for `With Phosphor Icon` and `With Custom Icon` 
- Verify that the Banner works with a PhosphorIcon (`?path=/story/packages-banner--with-phosphor-icon`)
- Verify that the Banner works with a custom icon (`?path=/story/packages-banner--with-custom-icon`)
- Verify that the aria-label of the icon matches the `kind` prop ("info", "success", "warning", "critical")
  - Note: I created WB-1713 since the aria label for the icon doesn't support translations at the moment

## Screenshots
Banner with Phosphor Icon
<img width="1728" alt="Banner with Phosphor Icon" src="https://github.com/Khan/wonder-blocks/assets/14334617/aee36007-d482-4d7a-9188-3bd1fa2b930c">

Banner with Custom Icon
<img width="1727" alt="Banner with Custom Icon" src="https://github.com/Khan/wonder-blocks/assets/14334617/f430f336-84b1-41f1-8078-02d837e55c51">

Author: beaesguerra

Reviewers: beaesguerra, jandrade

Required Reviewers:

Approved By: jandrade

Checks: ✅ codecov/project, ✅ Chromatic - Get results on regular PRs (ubuntu-latest, 20.x), ✅ Test (ubuntu-latest, 20.x, 2/2), ✅ Lint (ubuntu-latest, 20.x), ✅ Test (ubuntu-latest, 20.x, 1/2), ✅ Check build sizes (ubuntu-latest, 20.x), ✅ Prime node_modules cache for primary configuration (ubuntu-latest, 20.x), ✅ Check for .changeset entries for all changed files (ubuntu-latest, 20.x), ✅ Publish npm snapshot (ubuntu-latest, 20.x), ✅ gerald, ✅ Test (ubuntu-latest, 20.x, 2/2), ✅ Test (ubuntu-latest, 20.x, 1/2), ✅ Lint (ubuntu-latest, 20.x), ✅ Check build sizes (ubuntu-latest, 20.x), ✅ Chromatic - Build on regular PRs / chromatic (ubuntu-latest, 20.x), ⏭️  Publish npm snapshot, ✅ Prime node_modules cache for primary configuration (ubuntu-latest, 20.x), ⏭️  Chromatic - Skip on Release PR (changesets), ✅ Check for .changeset entries for all changed files (ubuntu-latest, 20.x), ✅ gerald, ⏭️  dependabot

Pull Request URL: #2249
  • Loading branch information
beaesguerra authored Jun 11, 2024
1 parent cc2d8e8 commit 9eecfc5
Show file tree
Hide file tree
Showing 6 changed files with 182 additions and 1 deletion.
5 changes: 5 additions & 0 deletions .changeset/thin-pants-double.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@khanacademy/wonder-blocks-banner": minor
---

Adds optional `icon` prop for the `Banner` component. An icon asset (either imported from Phosphor or a imported custom icon) can be used. If not provided, default icons are used to represent the `kind` prop.
12 changes: 12 additions & 0 deletions __docs__/wonder-blocks-banner/banner.argtypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import type {InputType} from "@storybook/csf";

import * as React from "react";

import {IconMappings} from "../wonder-blocks-icon/phosphor-icon.argtypes";

const actionsMappings = {
none: null,
buttons: [
Expand Down Expand Up @@ -111,4 +113,14 @@ export default {
},
type: {name: "string", required: false},
},
icon: {
control: {type: "select"},
options: Object.keys(IconMappings),
mapping: IconMappings,
table: {
type: {
summary: "PhosphorIconAsset | string",
},
},
},
} satisfies Record<string, InputType>;
64 changes: 64 additions & 0 deletions __docs__/wonder-blocks-banner/banner.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from "react";
import {StyleSheet} from "aphrodite";
import type {Meta, StoryObj} from "@storybook/react";
import magnifyingGlass from "@phosphor-icons/core/regular/magnifying-glass.svg";

import Button from "@khanacademy/wonder-blocks-button";
import {View} from "@khanacademy/wonder-blocks-core";
Expand All @@ -13,6 +14,7 @@ import Banner from "@khanacademy/wonder-blocks-banner";
import BannerArgTypes from "./banner.argtypes";
import ComponentInfo from "../../.storybook/components/component-info";
import packageConfig from "../../packages/wonder-blocks-banner/package.json";
import crownIcon from "../wonder-blocks-icon/icons/crown.svg";

type StoryComponentType = StoryObj<typeof Banner>;

Expand Down Expand Up @@ -468,6 +470,68 @@ export const WithMixedActions: StoryComponentType = {
),
};

/**
* Use the `icon` prop to show a specific Phosphor icon in the banner instead. If the
* `icon` prop is not set, a default icon will be used in the banner depending
* on the `kind` prop.
*
* __NOTE:__ Icons are available from the [Phosphor
* Icons](https://phosphoricons.com/) library.
*
* To use a Phosphor icon, you can use the following syntax:
*
* ```jsx
* import magnifyingGlass from "@phosphor-icons/core/regular/magnifying-glass.svg";
* <Banner icon={magnifyingGlass} layout="floating" text="text" />
* ```
*
* __Accessibility__: The icon chosen for the banner is decorative and
* will always have an `aria-label` that communicates the kind of banner
* (e.g. "info").
*/
export const WithPhosphorIcon: StoryComponentType = {
render: (args) => (
<Banner
icon={magnifyingGlass}
{...args}
layout="floating"
text="Here is an example with a Phosphor Icon"
/>
),
};

/**
* Use the `icon` prop to show a custom icon in the banner instead. If the
* `icon` prop is not set, a default icon will be used in the banner depending
* on the `kind` prop.
*
* To use a custom icon, you can use the following syntax:
*
* ```jsx
* // This SVG should have the following attributes:
* // - viewBox="0 0 256 256"
* // - fill="currentColor"
* // - A path (or paths) scaled up to fit in the 256x256 viewport.
*
* import crownIcon from "./icons/crown.svg";
* <Banner icon={crownIcon} layout="floating" text="text" />
* ```
*
* __Accessibility__: The icon chosen for the banner is decorative and
* will always have an `aria-label` that communicates the kind of banner
* (e.g. "info").
*/
export const WithCustomIcon: StoryComponentType = {
render: (args) => (
<Banner
icon={crownIcon}
{...args}
layout="floating"
text="Here is an example with a custom icon"
/>
),
};

/**
* When in the right-to-left direction, the banner is mirrored. This example has
* text in Urdu, which is a right-to-left language.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import * as React from "react";
import {render, screen} from "@testing-library/react";
import magnifyingGlass from "@phosphor-icons/core/regular/magnifying-glass.svg";

import Button from "@khanacademy/wonder-blocks-button";
import customIcon from "./custom-icon-mock.svg";

import Banner from "../banner";

Expand Down Expand Up @@ -449,4 +451,90 @@ describe("Banner", () => {
"This is a banner aria label.",
);
});

describe("Icon prop", () => {
test("uses icon prop when a Phosphor icon is provided", () => {
// Arrange

// Act
render(
<Banner
text="test text"
layout="full-width"
testId="wonder-blocks-banner-test-id"
aria-label="This is a banner aria label."
kind="warning"
icon={magnifyingGlass}
/>,
);

// Assert
expect(screen.getByTestId("banner-kind-icon")).toHaveStyle(
`mask-image: url(${magnifyingGlass});`,
);
});

test("uses icon prop when a custom icon is provided", () => {
// Arrange

// Act
render(
<Banner
text="test text"
layout="full-width"
testId="wonder-blocks-banner-test-id"
aria-label="This is a banner aria label."
kind="warning"
icon={customIcon}
/>,
);

// Assert
expect(screen.getByTestId("banner-kind-icon")).toHaveStyle(
`mask-image: url(${customIcon});`,
);
});
test("uses `kind` as aria-label for provided Phosphor icon", () => {
// Arrange

// Act
render(
<Banner
text="test text"
layout="full-width"
testId="wonder-blocks-banner-test-id"
aria-label="This is a banner aria label."
kind="warning"
icon={magnifyingGlass}
/>,
);

// Assert
expect(screen.getByTestId("banner-kind-icon")).toHaveAttribute(
"aria-label",
"warning",
);
});
test("uses `kind` as aria-label for provided custom icon", () => {
// Arrange

// Act
render(
<Banner
text="test text"
layout="full-width"
testId="wonder-blocks-banner-test-id"
aria-label="This is a banner aria label."
kind="warning"
icon={customIcon}
/>,
);

// Assert
expect(screen.getByTestId("banner-kind-icon")).toHaveAttribute(
"aria-label",
"warning",
);
});
});
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 12 additions & 1 deletion packages/wonder-blocks-banner/src/components/banner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,16 @@ type Props = {
* Test ID used for e2e testing.
*/
testId?: string;
/**
* An optional icon to display. This is a reference to the icon asset (imported as a
* static SVG file). If not provided, a default icon will be used based on
* the "kind" prop.
*
* It supports the following types:
* - `PhosphorIconAsset`: a reference to a Phosphor SVG asset.
* - `string`: an import referencing an arbitrary SVG file.
*/
icon?: PhosphorIconAsset | string;
};

const getValuesForKind = (kind: BannerKind): BannerValues => {
Expand Down Expand Up @@ -186,6 +196,7 @@ const Banner = (props: Props): React.ReactElement => {
layout,
text,
testId,
icon,
} = props;

const renderActions = () => {
Expand Down Expand Up @@ -253,7 +264,7 @@ const Banner = (props: Props): React.ReactElement => {
/>
<View style={styles.containerInner}>
<PhosphorIcon
icon={valuesForKind.icon}
icon={icon || valuesForKind.icon}
size="medium"
style={styles.icon}
aria-label={kind}
Expand Down

0 comments on commit 9eecfc5

Please sign in to comment.