Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ThemeProvider: add component & add cambio versions of Button / IconButton & LinkButton #317

Merged
merged 6 commits into from Mar 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/dull-pandas-build.md
@@ -0,0 +1,7 @@
---
"@cambly/syntax-design-tokens": minor
"@cambly/syntax-core": minor
"@syntax/storybook": minor
---

Add ThemeProvider & styles for Button / IconButton & LinkButton
3 changes: 2 additions & 1 deletion apps/storybook/.storybook/main.ts
Expand Up @@ -13,9 +13,10 @@ const config: StorybookConfig = {
actions: false,
},
},
"@storybook/addon-a11y",
"@storybook/addon-actions",
"@storybook/addon-toolbars",
"storybook-addon-designs",
"@storybook/addon-a11y",
"storybook-addon-rtl",
],
staticDirs: ["../public"],
Expand Down
31 changes: 28 additions & 3 deletions apps/storybook/.storybook/preview.tsx
@@ -1,7 +1,32 @@
import React from "react";
import "@cambly/syntax-design-tokens/dist/css/variables.css";
import { initializeRTL } from "storybook-addon-rtl";
import { Preview } from "@storybook/react";
import ThemeProvider from "../../../packages/syntax-core/src/ThemeProvider/ThemeProvider";

initializeRTL();
const preview: Preview = {
globalTypes: {
theme: {
description: "Global theme for components",
defaultValue: "classic",
toolbar: {
title: "Theme",
icon: "circlehollow",
items: ["classic", "cambio"],
dynamicTitle: true,
},
},
},
decorators: [
(Story, context) => {
const theme = context.globals.theme;
return (
<ThemeProvider themeName={theme}>
<Story />
</ThemeProvider>
);
},
],
};

export default preview;

export const decorators = [(Story) => <Story />];
2 changes: 2 additions & 0 deletions apps/storybook/package.json
Expand Up @@ -27,6 +27,7 @@
"@storybook/addon-docs": "7.0.7",
"@storybook/addon-essentials": "7.0.7",
"@storybook/addon-links": "7.0.7",
"@storybook/addon-toolbars": "^7.6.17",
"@storybook/addons": "7.0.7",
"@storybook/react": "7.0.7",
"@storybook/react-vite": "7.0.7",
Expand All @@ -36,6 +37,7 @@
"serve": "14.2.0",
"storybook": "7.0.7",
"storybook-addon-designs": "7.0.0-beta.2",
"storybook-addon-rtl": "^1.0.0",
"storybook-dark-mode": "3.0.0",
"typescript": "5.0.4",
"vite": "4.3.4"
Expand Down
13 changes: 12 additions & 1 deletion apps/storybook/stories/Colors.tsx
Expand Up @@ -4,7 +4,10 @@ import variables from "../../../packages/syntax-design-tokens/dist/json/variable
export default function Colors() {
const groupColors = Object.entries(variables).reduce(
(acc, [key, value]) => {
if (
if (key.includes("cambio")) {
// @ts-expect-error
acc["Cambio"].push({ key, value });
} else if (
key.includes("black") ||
key.includes("white") ||
key.includes("gray")
Expand All @@ -25,6 +28,7 @@ export default function Colors() {
return acc;
},
{
Cambio: [],
Grayscale: [],
Primary: [],
Destructive: [],
Expand All @@ -46,8 +50,15 @@ export default function Colors() {
style={{
color:
[
"color-cambio-white",
"color-cambio-cream",
"color-cambio-pink",
"color-cambio-sky",
"color-cambio-yellow-700",
"color-cambio-transparent-full",
"color-base-gray-10",
"color-base-gray-30",
"color-base-yellow-700",
"color-base-white",
].includes(key) ||
(number && parseInt(number) < 400)
Expand Down
1 change: 0 additions & 1 deletion package.json
Expand Up @@ -26,7 +26,6 @@
"glob": "10.3.10",
"jsdom": "22.0.0",
"prettier": "2.8.8",
"storybook-addon-rtl": "^0.5.0",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nathan-ong @somethiiing fyi follow up from
https://github.com/Cambly/syntax/pull/289/files#diff-7ae45ad102eab3b6d7e7896acd08c427a9b25b346470d7bc6507b6481575d519R29

Storybook specific dependencies should go inside the storybook app so moved it to apps/storybook/package.json

"stylelint": "15.6.0",
"stylelint-config-css-modules": "4.2.0",
"stylelint-config-standard": "33.0.0",
Expand Down
21 changes: 21 additions & 0 deletions packages/syntax-core/src/Button/Button.module.css
Expand Up @@ -72,6 +72,27 @@
border-radius: 16px;
}

.smCambio {
min-width: 52px;
height: 32px;
padding: 0 16px;
border-radius: 100px;
}

.mdCambio {
min-width: 78px;
height: 48px;
padding: 0 24px;
border-radius: 100px;
}

.lgCambio {
min-width: 104px;
height: 64px;
padding: 0 32px;
border-radius: 100px;
}

.icon {
color: inherit;
}
Expand Down
3 changes: 3 additions & 0 deletions packages/syntax-core/src/Button/Button.stories.tsx
Expand Up @@ -21,6 +21,9 @@ export default {
"destructive-secondary",
"destructive-tertiary",
"success",
"success-primary",
"success-secondary",
"success-tertiary",
"branded",
"inverse",
],
Expand Down
77 changes: 60 additions & 17 deletions packages/syntax-core/src/Button/Button.tsx
@@ -1,18 +1,23 @@
import React, { forwardRef } from "react";
import classNames from "classnames";

import backgroundColor from "../colors//backgroundColor";
import foregroundColor from "../colors/foregroundColor";
import foregroundTypographyColor from "../colors/foregroundTypographyColor";
import { type Size } from "../constants";
import Typography from "../Typography/Typography";
import Box from "../Box/Box";

import iconSize from "./constants/iconSize";
import textVariant from "./constants/textVariant";
import loadingIconSize from "./constants/loadingIconSize";
import styles from "./Button.module.css";
import useIsHydrated from "../useIsHydrated";
import { useTheme } from "../ThemeProvider/ThemeProvider";
import { classicColor, cambioColor } from "./constants/color";
import {
classicBackgroundColor,
cambioBackgroundColor,
} from "../colors/backgroundColor";
import {
classicForegroundColor,
cambioForegroundColor,
} from "../colors/foregroundColor";

type ButtonProps = {
/**
Expand All @@ -30,6 +35,15 @@ type ButtonProps = {
/**
* The color of the button
*
* Classic only:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it a typo that success-primary and success-secondary are in both the Classic and Cambio portion of the comment?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zprong updated - success is classic only, success-primary, success-secondary and success-tertiary are cambio only

* * `inverse`
* * `success`
*
* Cambio only:
* * `success-primary`
* * `success-secondary`
* * `success-tertiary`
*
* @defaultValue "primary"
*/
color?:
Expand All @@ -41,14 +55,23 @@ type ButtonProps = {
| "destructive-tertiary"
| "branded"
| "success"
| "success-primary"
| "success-secondary"
| "success-tertiary"
| "inverse";
/**
* The size of the button
*
* Classic:
* * `sm`: 32px
* * `md`: 40px
* * `lg`: 48px
*
* Cambio:
* * `sm`: 32px
* * `md`: 48px
* * `lg`: 64px
*
* @defaultValue "md"
*/
size?: (typeof Size)[number];
Expand Down Expand Up @@ -76,10 +99,12 @@ type ButtonProps = {
fullWidth?: boolean;
/**
* The icon to be displayed at the start of the button. Please use a [Rounded Material Icon](https://material.io/resources/icons/?style=round)
* Note: startIcon is not supported in the Cambio theme
*/
startIcon?: React.ComponentType<{ className?: string }>;
/**
* The icon to be displayed at the end of the button. Please use a [Rounded Material Icon](https://material.io/resources/icons/?style=round)
* Note: endIcon is not supported in the Cambio theme
*/
endIcon?: React.ComponentType<{ className?: string }>;
/**
Expand Down Expand Up @@ -120,6 +145,17 @@ const Button = forwardRef<HTMLButtonElement, ButtonProps>(
ref,
) => {
const isHydrated = useIsHydrated();
const { themeName } = useTheme();

const foregroundColorClass =
themeName === "classic"
? classicForegroundColor(classicColor(color))
: cambioForegroundColor(cambioColor(color));

const backgroundColorClass =
themeName === "classic"
? classicBackgroundColor(classicColor(color))
: cambioBackgroundColor(cambioColor(color));

return (
<button
Expand All @@ -132,39 +168,46 @@ const Button = forwardRef<HTMLButtonElement, ButtonProps>(
onClick={onClick}
className={classNames(
styles.button,
foregroundColor(color),
backgroundColor(color),
styles[size],
foregroundColorClass,
backgroundColorClass,
themeName === "classic" ? styles[size] : styles[`${size}Cambio`],
{
[styles.fullWidth]: fullWidth,
[styles.buttonGap]: size === "lg" || size === "md",
[styles.secondaryBorder]: color === "secondary",
[styles.buttonGap]:
themeName === "classic" && (size === "lg" || size === "md"),
[styles.secondaryBorder]:
themeName === "classic" && color === "secondary",
[styles.secondaryDestructiveBorder]:
color === "destructive-secondary",
themeName === "classic" && color === "destructive-secondary",
},
)}
>
{!loading && StartIcon && (
{!loading && StartIcon && themeName === "classic" && (
<StartIcon className={classNames(styles.icon, iconSize[size])} />
)}
{((loading && loadingText) || (!loading && text)) && (
<Box paddingX={1}>
<Typography
size={textVariant[size]}
color={foregroundTypographyColor(color)}
size={
themeName === "classic" ? textVariant[size] : textVariant[size]
}
>
<span style={{ fontWeight: 500 }}>
<span
// Temporary - until we have cambio colors on Typogrphay
className={foregroundColorClass}
style={{ fontWeight: 500 }}
>
{loading ? loadingText : text}
</span>
</Typography>
</Box>
)}
{!loading && EndIcon && (
{!loading && EndIcon && themeName === "classic" && (
<EndIcon className={classNames(styles.icon, iconSize[size])} />
)}
{loading && (
<svg
className={classNames(styles.loading, foregroundColor(color))}
className={classNames(styles.loading, foregroundColorClass)}
viewBox="22 22 44 44"
width={loadingIconSize[size]}
height={loadingIconSize[size]}
Expand Down
5 changes: 5 additions & 0 deletions packages/syntax-core/src/Button/constants/classicSize.ts
@@ -0,0 +1,5 @@
export default function classicSize(
size: "sm" | "md" | "lg" | "xl",
): "sm" | "md" | "lg" {
return size === "xl" ? "lg" : size;
}
69 changes: 69 additions & 0 deletions packages/syntax-core/src/Button/constants/color.ts
@@ -0,0 +1,69 @@
export function classicColor(
color:
| "primary"
| "secondary"
| "tertiary"
| "destructive-primary"
| "destructive-secondary"
| "destructive-tertiary"
| "branded"
| "success"
| "success-primary"
| "success-secondary"
| "success-tertiary"
| "inverse",
):
| "primary"
| "secondary"
| "tertiary"
| "destructive-primary"
| "destructive-secondary"
| "branded"
| "success"
| "inverse" {
if (color === "destructive-tertiary") {
return "destructive-secondary";
} else if (
color === "success-primary" ||
color === "success-secondary" ||
color === "success-tertiary"
) {
return "success";
}
return color;
}

export function cambioColor(
color:
| "primary"
| "secondary"
| "tertiary"
| "destructive-primary"
| "destructive-secondary"
| "destructive-tertiary"
| "branded"
| "success"
| "success-primary"
| "success-secondary"
| "success-tertiary"
| "inverse",
):
| "primary"
| "secondary"
| "tertiary"
| "destructive-primary"
| "destructive-secondary"
| "destructive-tertiary"
| "branded"
| "success-primary"
| "success-secondary"
| "success-tertiary" {
if (color === "success") {
return "success-primary";
}
// TODO - validate with AJ
else if (color === "inverse") {
return "secondary";
}
return color;
}