-
Notifications
You must be signed in to change notification settings - Fork 221
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
feat(labs): Theming (react) #272
Merged
Merged
Changes from 52 commits
Commits
Show all changes
53 commits
Select commit
Hold shift + click to select a range
99ea29f
feat: Proof of concept of theming
anicholls ad33cf3
refactor: Move all theming into a new folder in core
anicholls 44f4260
feat(core): Add the ability to override default Canvas theme
anicholls 9e1cff6
feat(core): Iterate on theme structure
anicholls 2fa4f7a
feat(banner): Implement theming
anicholls 7517a24
feat(core): Add story for default theme
anicholls a2783c6
feat(core): Add helper to get theme if ThemeProvider doesn't exist
anicholls db2ba94
chore(avatar): Remove theme test component
anicholls 862104e
feat: Add theme as a knob to customize in real time
anicholls 74a264f
fix: Add keys to resolve warning in storybook
anicholls 540c33c
docs(core): Add to CanvasProvider docs
anicholls 5c848b5
docs(core): Update provider documenation
anicholls 615e874
feat(core): Add breakpoints to theme
anicholls cb1a662
refactor(labs): Move theming to labs
anicholls fd5370e
chore: Restore InputProviderDecorator
anicholls e144f89
docs: Update docs in core and labs core to reflect the move
anicholls 8016e88
fix(banner): Remove unnecessary console log
anicholls 0c7c47b
docs(labs): Add breakpoints to theming docs
anicholls 97814e7
feat(labs): Add support for window theme object
anicholls 34748ee
refactor(labs): Rename getTheme to useTheme so it's more hook-like
anicholls 3c1645b
fix(labs): Lock the keys of breakpoints with as const
anicholls ac510aa
refactor(labs): Expand on the theme object
anicholls 1f28948
feat(labs): Add color expansion support to theme object
anicholls 1addd9e
fix(labs): Fix type error in theme story
anicholls 1da1279
feat(labs): Prevent error from chroma on invalid input color
anicholls b78bb5b
fix(labs): Only expand main color if the others aren't defined
anicholls 15706bc
feat(labs): Use enum for color direction to make shift fn more readable
anicholls a0b7487
fix(labs): Remove unnecessary breakpoints index signature
anicholls c69da38
chore: Cleanup after rebase
anicholls 2d80f98
refactor: Move useTheme into its own file
anicholls 30934a1
fix(labs): Allow createCanvasTheme to be called without a palette
anicholls b84e3da
docs(labs): Fix readme typo
anicholls 60c333f
refactor(labs): Decouple window theme from CanvasProvider
anicholls 7ebbb17
docs(labs): Document theme breakpoint functionality
anicholls 8b3b000
feat(labs): Add styled function to avoid explicitly useTheme() call
anicholls 796486c
fix(labs): Add missing window global after it got removed
anicholls 8a26547
test(labs): Add tests for useTheme and createCanvasTheme
anicholls b6b527e
chore: Undo formatting changes in storybook config
anicholls 6de7c2e
fix(labs): Use lodash merge instead of deepmerge
anicholls 6eff5a2
fix(labs): Add missing deps
anicholls 376332a
docs: Update documentation
anicholls 778eaab
refactor(labs): Roll our own ThemeProvider
anicholls 05f40cb
chore: Remove emotion-theming dep since we're no longer using it
anicholls fe15672
fix: Lock emotion-theming at 10.0.10
anicholls 01d336d
refactor(labs): Update window.wdCanvas.theme to match existing usage
anicholls 28844c6
chore: Update chroma-js version
anicholls 6c2d525
feat(core): Make InputProvider noop if it's nested within another
anicholls 821c1de
Merge branch 'master' into theming
anicholls 2da8610
fix(core): Add missing initializer to InputProvider
anicholls ad34a51
Merge branch 'master' into theming
anicholls fe60406
Merge branch 'master' into theming
anicholls fe2aea0
refactor(banner): Remove theme functionality from Banner for now
anicholls 251aee0
chore: Revert changes to avatar and banner
anicholls File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,23 @@ | ||
import type from './lib/type'; | ||
import space from './lib/space'; | ||
import CanvasProvider from './lib/CanvasProvider'; | ||
import {breakpoints, CanvasBreakpoints, BreakpointKey} from './lib/theming/breakpoints'; | ||
import createCanvasTheme from './lib/theming/createCanvasTheme'; | ||
import styled from './lib/theming/styled'; | ||
import useTheme from './lib/theming/useTheme'; | ||
|
||
export default type; | ||
export {type, space}; | ||
export { | ||
breakpoints, | ||
type, | ||
space, | ||
BreakpointKey, | ||
CanvasBreakpoints, | ||
CanvasProvider, | ||
createCanvasTheme, | ||
styled, | ||
useTheme, | ||
}; | ||
export * from './lib/type'; | ||
export * from './lib/theming/types'; | ||
export * from './lib/theming/theme'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import * as React from 'react'; | ||
import {InputProvider} from '@workday/canvas-kit-react-core'; | ||
import {CanvasTheme} from './theming/types'; | ||
import {ThemeProvider, defaultCanvasTheme} from './theming/theme'; | ||
|
||
export interface CanvasProviderProps { | ||
theme: CanvasTheme; | ||
} | ||
|
||
export default class CanvasProvider extends React.Component<CanvasProviderProps> { | ||
static defaultProps = { | ||
theme: defaultCanvasTheme, | ||
}; | ||
|
||
render() { | ||
const {children, theme} = this.props; | ||
|
||
return ( | ||
<ThemeProvider value={theme}> | ||
<InputProvider>{children}</InputProvider> | ||
</ThemeProvider> | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,253 @@ | ||
# Canvas Kit Theming | ||
|
||
Canvas Kit Core contains wrappers and types to enabling theming of Canvas components. | ||
|
||
## Installation | ||
|
||
```sh | ||
yarn add @workday/canvas-kit-labs-react-core | ||
``` | ||
|
||
or | ||
|
||
```sh | ||
yarn add @workday/canvas-kit-labs-react-core | ||
``` | ||
|
||
## Usage | ||
|
||
Wrap all of your Canvas components with the `CanvasProvider` higher order component. Usually this | ||
would go in the highest level component of your application. This includes an | ||
[`InputProvider`](../../README.md#input-provider) and includes all global configuration needed for | ||
our Canvas Components. | ||
|
||
```tsx | ||
import * as React from 'react'; | ||
import {CanvasProvider} from '@workday/canvas-kit-labs-react-core'; | ||
|
||
<CanvasProvider>{/* All your components containing any Canvas components */}</CanvasProvider>; | ||
``` | ||
|
||
## Component Props | ||
|
||
### Required | ||
|
||
> None | ||
|
||
### Optional | ||
|
||
#### `theme: CanvasTheme` | ||
|
||
> The theme to be used throughout the children of the `CanvasProvider` component. | ||
|
||
Default: `defaultCanvasTheme` | ||
|
||
## Theme Object | ||
|
||
The Canvas theme is based on the following object: | ||
|
||
```ts | ||
export const defaultCanvasTheme: CanvasTheme = { | ||
palette: { | ||
primary: { | ||
lightest: colors.blueberry100, | ||
light: colors.blueberry200, | ||
main: colors.blueberry400, | ||
dark: colors.blueberry500, | ||
darkest: colors.blueberry600, | ||
contrast: colors.frenchVanilla100, | ||
}, | ||
alert: { | ||
lightest: colors.cantaloupe100, | ||
light: colors.cantaloupe300, | ||
main: colors.cantaloupe400, | ||
dark: colors.cantaloupe500, | ||
darkest: colors.cantaloupe600, | ||
contrast: colors.frenchVanilla100, | ||
}, | ||
error: { | ||
lightest: colors.cinnamon100, | ||
light: colors.cinnamon300, | ||
main: colors.cinnamon500, | ||
dark: colors.cinnamon600, | ||
darkest: '#80160E', | ||
contrast: colors.frenchVanilla100, | ||
}, | ||
success: { | ||
lightest: colors.greenApple100, | ||
light: colors.greenApple300, | ||
main: colors.greenApple600, | ||
dark: '', | ||
darkest: '', | ||
contrast: colors.frenchVanilla100, | ||
}, | ||
neutral: { | ||
lightest: colors.soap200, | ||
light: colors.soap300, | ||
main: colors.soap600, | ||
dark: colors.licorice300, | ||
darkest: colors.licorice400, | ||
contrast: colors.frenchVanilla100, | ||
}, | ||
common: { | ||
focusOutline: colors.blueberry400, | ||
}, | ||
}, | ||
breakpoints: { | ||
values: { | ||
zero: 0, | ||
s: 600, | ||
m: 960, | ||
l: 1280, | ||
xl: 1920, | ||
}, | ||
up, | ||
down, | ||
between, | ||
only, | ||
}, | ||
}; | ||
``` | ||
|
||
Any changes will be reflected across all supported components. You are also able to consume for your | ||
own use cases. | ||
|
||
## Custom Theme | ||
anicholls marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
The `CanvasProvider` accepts a full or partial theme object to give a branded look to the component | ||
library. Pass your theme into `createCanvasTheme` and use the return value for the `theme` prop. | ||
|
||
If you only set a `main` color, the rest of the respective palette will be automatically generated | ||
(note text `contrast` color will always return white if not specified). | ||
|
||
Example: | ||
|
||
```tsx | ||
import { | ||
CanvasProvider, | ||
PartialCanvasTheme, | ||
createCanvasTheme, | ||
} from '@workday/canvas-kit-labs-react-core'; | ||
|
||
const theme: PartialCanvasTheme = { | ||
palette: { | ||
primary: { | ||
main: colors.cantaloupe400, | ||
}, | ||
}, | ||
}; | ||
|
||
<CanvasProvider theme={createCanvasTheme(theme)}> | ||
{/* Your app with Canvas components */} | ||
</CanvasProvider>; | ||
``` | ||
|
||
### Nesting Theme Providers | ||
|
||
It is possible to set a theme for a specific component or set of components within your React tree. | ||
This is generally discouraged for consistency reasons, but may be required in some contexts (a green | ||
`Switch` component for example). To do this, you must use the `ThemeProvider` component. The inner | ||
theme will override the outer theme. | ||
|
||
```tsx | ||
import * as React from 'react'; | ||
import {CanvasProvider, CanvasTheme, ThemeProvider} from '@workday/canvas-kit-labs-react-core'; | ||
import {Switch} from '@workday/canvas-kit-react-switch'; | ||
|
||
const theme: PartialCanvasTheme = { | ||
palette: { | ||
primary: { | ||
main: colors.greenApple400, | ||
}, | ||
}, | ||
}; | ||
|
||
<CanvasProvider> | ||
{/* All your components containing any Canvas components */} | ||
<ThemeProvider value={createCanvasTheme(theme)}> | ||
<Switch checked={true} /> | ||
</ThemeProvider> | ||
</CanvasProvider>; | ||
``` | ||
|
||
### Context Alternative | ||
|
||
If, for whatever reason, you're not able to use React Contexts, we offer an alternative. A good | ||
example of this is an app with many small React trees. | ||
|
||
Simply set your theme on the window object like so: | ||
|
||
```tsx | ||
// If using typescript, you will need to declare this on the window object | ||
declare global { | ||
interface Window { | ||
workday: { | ||
canvas: { | ||
theme?: CanvasTheme; | ||
}; | ||
}; | ||
} | ||
} | ||
|
||
const theme: PartialCanvasTheme = { | ||
palette: { | ||
primary: { | ||
main: colors.greenApple400, | ||
}, | ||
}, | ||
}; | ||
|
||
window.workday.canvas.theme = createCanvasTheme(theme); | ||
``` | ||
|
||
Note if any of the window object hasn't been defined, you will need to change your assignment. For | ||
example: | ||
|
||
```tsx | ||
window.workday = { | ||
canvas: { | ||
theme: createCanvasTheme(theme); | ||
} | ||
} | ||
``` | ||
|
||
If the theme is not available via a context, Canvas Kit components will attempt to pull it from this | ||
variable before falling back to the default theme. | ||
|
||
## Breakpoints | ||
|
||
Our breakpoint system is customized within the theme object. `theme.breakpoints.values` contains the | ||
various widths that our components adjust at: | ||
|
||
| Name | Size (px) | | ||
| ------ | --------- | | ||
| `zero` | 0 | | ||
| `s` | 600 | | ||
| `m` | 960 | | ||
| `l` | 1280 | | ||
| `xl` | 1920 | | ||
|
||
There are also several functions to help with generating media queries: | ||
|
||
#### `up: (key: BreakpointFnParam) => string` | ||
|
||
> Returns a media query reflecting your specified size and up. Works with the enum (e.g. | ||
> BreakpointKey.m) or the string (e.g. 'm'). Example: theme.breakpoints.up(BreakpointKey.m) => | ||
> '@media (min-width:960px)' | ||
|
||
#### `down: (key: BreakpointFnParam) => string` | ||
|
||
> Returns a media query reflecting your specified size and down. Works with the enum or the string | ||
> (e.g. 'm'). Example: theme.breakpoints.down(BreakpointKey.m) => '@media (max-width:1279.5px)' | ||
|
||
#### `between: (start: BreakpointFnParam, end: BreakpointFnParam) => string` | ||
|
||
> Returns a media query reflecting the sizes between your specified breakpoints. Works with the enum | ||
> or the string (e.g. 'm'). Example: theme.breakpoints.between(BreakpointKey.m, BreakpointKey.l) => | ||
> '@media (min-width:960px) and (max-width:1919.5px)' | ||
|
||
#### `only: (key: BreakpointFnParam) => string` | ||
|
||
> Returns a media query reflecting the size swithin your specified breakpoint. Works with the enum | ||
> or the string (e.g. 'm'). Example: theme.breakpointsonly(BreakpointKey.m) => '@media | ||
> (min-width:960px) and (max-width:1279.5px)' |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Have we tested multiple instances of
InputProvider
?If you're using
CanvasProvider
in different subtrees, it'll always come with anInputProvider
and could potentially be unnecessarily nested. I'm not sure ourInputProvider
noops or does some performance optimization if it's already been used higher up the tree?Two possible solutions (I'm sure there are more):
InputProvider
wraps around the children or is omitted completely (what would it default to?)InputProvider
inThemeProvider
if it's meant to go in the top level tree. Because if we were to do something like<CanvasProvider inputProvider={false}>...</CanvasProvider>
then what isCanvasProvider
really doing at this point other than providing a default theme?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.
Multiple instances of
InputProvider
will work fine from a consumer level, but I can't speak to the performance implications of doing that.We could easily add a check in
InputProvider
to noop if one exists higher in the tree. This would be my vote rather than adding prop complexity toCanvasProvider
or removingCanvasProvider
completely.I realize
CanvasProvider
is not doing much right now. However, there are two reasons I would like to keep it:is much simpler and hides our noise, compared to:
Anyway, let me know if you agree. I will probably add that functionality to
InputProvider
either way.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.
@lychyi noop added to
InputProvider
in 6c2d525