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
Changes from all commits
50820ae
8920a56
3c442f7
085f3f1
3b4c21f
0efb08e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
--- | ||
"@cambly/syntax-design-tokens": minor | ||
"@cambly/syntax-core": minor | ||
"@syntax/storybook": minor | ||
--- | ||
|
||
Add ThemeProvider & styles for Button / IconButton & LinkButton |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 />]; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 = { | ||
/** | ||
|
@@ -30,6 +35,15 @@ type ButtonProps = { | |
/** | ||
* The color of the button | ||
* | ||
* Classic only: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it a typo that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @zprong updated - |
||
* * `inverse` | ||
* * `success` | ||
* | ||
* Cambio only: | ||
* * `success-primary` | ||
* * `success-secondary` | ||
* * `success-tertiary` | ||
* | ||
* @defaultValue "primary" | ||
*/ | ||
color?: | ||
|
@@ -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]; | ||
|
@@ -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 }>; | ||
/** | ||
|
@@ -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 | ||
|
@@ -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]} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export default function classicSize( | ||
size: "sm" | "md" | "lg" | "xl", | ||
): "sm" | "md" | "lg" { | ||
return size === "xl" ? "lg" : size; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} |
There was a problem hiding this comment.
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 toapps/storybook/package.json