Skip to content

Commit

Permalink
WB-1755: Toolbar - Allow title to also support React nodes (#2313)
Browse files Browse the repository at this point in the history
## Summary:

There’s a valid use case where a toolbar needs to use a progress bar in the
center area. Right now, that part of the layout only accepts a title as a
string.

See design screenshot for reference:

<img width="1205" alt="Screenshot 2024-08-30 at 1 56 40 PM" src="https://github.com/user-attachments/assets/4b81c8f5-6f2b-4517-9f5e-e5d311a475a1">


This change allows the title to also accept React nodes, which will allow
engineers to use more complex components in that part of the layout.

Issue: https://khanacademy.atlassian.net/browse/WB-1755

## Test plan:

Navigate to the new story in the toolbar documentation and verify that the
progress bar is displayed in the center area.

/?path=/docs/packages-toolbar--docs#custom%20toolbar

<img width="1081" alt="Screenshot 2024-08-30 at 1 56 15 PM" src="https://github.com/user-attachments/assets/beec88e2-c511-47d9-a79a-b11d071debc0">

Author: jandrade

Reviewers: jandrade, kevinb-khan, beaesguerra

Required Reviewers:

Approved By: kevinb-khan, beaesguerra

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

Pull Request URL: #2313
  • Loading branch information
jandrade authored Sep 9, 2024
1 parent af62ec3 commit 30b6728
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 69 deletions.
5 changes: 5 additions & 0 deletions .changeset/tame-mice-lick.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@khanacademy/wonder-blocks-toolbar": minor
---

Allow title to also support React nodes
18 changes: 18 additions & 0 deletions __docs__/wonder-blocks-toolbar/toolbar.argtypes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import magnifyingGlassPlus from "@phosphor-icons/core/regular/magnifying-glass-p
import magnifyingGlassMinus from "@phosphor-icons/core/regular/magnifying-glass-minus.svg";

import Button from "@khanacademy/wonder-blocks-button";
import {View} from "@khanacademy/wonder-blocks-core";
import IconButton from "@khanacademy/wonder-blocks-icon-button";
import {Strut} from "@khanacademy/wonder-blocks-layout";
import Link from "@khanacademy/wonder-blocks-link";
Expand Down Expand Up @@ -45,6 +46,23 @@ export const leftContentMappings: Mappings = {
<IconButton icon={magnifyingGlassPlus} kind="primary" />
</>
),
exitWithTitle: (
<View
style={{
flexDirection: "row",
gap: spacing.medium_16,
placeItems: "center",
}}
>
<Button startIcon={xIcon} kind="tertiary">
Exit
</Button>
|
<LabelLarge>
Algebra Test<sup>BETA</sup>
</LabelLarge>
</View>
),
};

const buttonStyle = {width: 160} as const;
Expand Down
26 changes: 25 additions & 1 deletion __docs__/wonder-blocks-toolbar/toolbar.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {StyleSheet} from "aphrodite";
import type {Meta, StoryObj} from "@storybook/react";

import {View} from "@khanacademy/wonder-blocks-core";
import {spacing} from "@khanacademy/wonder-blocks-tokens";
import {semanticColor, spacing} from "@khanacademy/wonder-blocks-tokens";

import Toolbar from "@khanacademy/wonder-blocks-toolbar";
import packageConfig from "../../packages/wonder-blocks-toolbar/package.json";
Expand Down Expand Up @@ -211,3 +211,27 @@ Dark.parameters = {
},
},
};

/**
* Sometimes we need to have a custom toolbar to include a custom component or a
* more complex layout in the center of the toolbar. This can be achieved by
* passing a React node to the `title` prop.
*
* **NOTE:** This approach should be used with caution, as it may break the
* layout of the toolbar.
*/
export const CustomToolbar: StoryComponentType = {
args: {
leftContent: leftContentMappings.exitWithTitle,
rightContent: rightContentMappings.primaryButton,
title: (
<View
style={{
width: 300,
height: spacing.xSmall_8,
background: semanticColor.mastery.primary,
}}
/>
),
},
};
126 changes: 58 additions & 68 deletions packages/wonder-blocks-toolbar/src/components/toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,22 +40,16 @@ type Props = AriaProps & {
*/
subtitle?: string;
/**
* The main title rendered in larger bold text.
* The main title rendered in larger bold text. It also supports rendering
* React nodes (use with caution).
*/
title?: string;
title?: string | React.ReactNode;
/**
* Test ID used for e2e testing.
*/
testId?: string;
};

type DefaultProps = {
color: Props["color"];
leftContent: Props["leftContent"];
rightContent: Props["rightContent"];
size: Props["size"];
};

/**
* The `Toolbar` component is a generic toolbar wrapper that exposes
* customization options. An optional `title` and `subtitle` property can be
Expand All @@ -73,74 +67,70 @@ type DefaultProps = {
* />
* ```
*/
export default class Toolbar extends React.Component<Props> {
static defaultProps: DefaultProps = {
color: "light",
leftContent: null,
rightContent: null,
size: "medium",
};

render(): React.ReactNode {
const {color, leftContent, rightContent, size, subtitle, title} =
this.props;

const TitleComponent = subtitle ? LabelLarge : HeadingSmall;
export default function Toolbar({
color = "light",
leftContent,
rightContent,
size = "medium",
subtitle,
title,
}: Props): React.ReactElement {
const TitleComponent = subtitle ? LabelLarge : HeadingSmall;

return (
return (
<View
style={[
sharedStyles.container,
color === "dark" && sharedStyles.dark,
size === "small" && sharedStyles.small,
]}
>
<View
style={[
sharedStyles.container,
color === "dark" && sharedStyles.dark,
size === "small" && sharedStyles.small,
sharedStyles.column,
sharedStyles.leftColumn,
title ? sharedStyles.withTitle : null,
]}
>
<View
style={[
sharedStyles.column,
sharedStyles.leftColumn,
title ? sharedStyles.withTitle : null,
]}
>
{leftContent}
</View>
{leftContent}
</View>

{title && (
<View
style={[sharedStyles.column, sharedStyles.wideColumn]}
>
<View
style={[sharedStyles.titles, sharedStyles.center]}
>
<TitleComponent id="wb-toolbar-title">
{title}
</TitleComponent>
{subtitle && (
<LabelSmall
style={
color === "light" &&
sharedStyles.subtitle
}
>
{subtitle}
</LabelSmall>
)}
</View>
{title && typeof title === "string" ? (
<View style={[sharedStyles.column, sharedStyles.wideColumn]}>
<View style={[sharedStyles.titles, sharedStyles.center]}>
<TitleComponent id="wb-toolbar-title">
{title}
</TitleComponent>
{subtitle && (
<LabelSmall
style={
color === "light" && sharedStyles.subtitle
}
>
{subtitle}
</LabelSmall>
)}
</View>
)}

<View
style={[
sharedStyles.column,
sharedStyles.rightColumn,
title ? sharedStyles.withTitle : null,
]}
>
{rightContent}
</View>
) : (
// We don't use wideColumn here to allow more flexibility with
// the custom node.
<View style={[sharedStyles.column]}>
<View style={[sharedStyles.titles]}>{title}</View>
</View>
)}

<View
style={[
sharedStyles.column,
sharedStyles.rightColumn,
title ? sharedStyles.withTitle : null,
]}
>
{rightContent}
</View>
);
}
</View>
);
}

const sharedStyles = StyleSheet.create({
Expand Down

0 comments on commit 30b6728

Please sign in to comment.