mirror of https://github.com/grafana/grafana
Docs: How to work with themes (#17876)
* Add docs for themes * Edit description a little bit * Update themes.mdpull/17895/head
parent
a2c1462675
commit
498572d2d8
@ -0,0 +1,103 @@ |
||||
## Core changes |
||||
|
||||
JS is the primary source of theme variables for Grafana. Theme definitions are located in `@grafana/ui/themes` directory. |
||||
|
||||
#### Themes are implemented in pure js. |
||||
|
||||
This is because our goal is to share variables between app and SASS. To achieve that themes are necessary during build time to be exposed to sass loader via `node-sass` functions (https://github.com/sass/node-sass/blob/master/README.md#functions--v300---experimental). This retrieval is implemented in `getThemeVariable(variablePath, themeName)`. |
||||
|
||||
#### Themes are available to React components via `ThemeContext` |
||||
|
||||
Context is available via `import { ThemeContext } from '@grafana/ui';` |
||||
|
||||
**If you skip `themeName` param, then dark theme's variant will be used** |
||||
|
||||
## Using themes in Grafana |
||||
|
||||
### SASS |
||||
|
||||
`getThemeVariable` is a function, that's available in sass files. Use it i.e. like this: |
||||
|
||||
```scss |
||||
// In theme agnostic SASS file |
||||
.foo { |
||||
font-size: getThemeVariable('typography.size.m'); |
||||
} |
||||
|
||||
// In *.[themeName].scss |
||||
.bar { |
||||
background-color: getThemeVariable('colors.blueLight', '[themeName]'); |
||||
} |
||||
``` |
||||
|
||||
### React |
||||
|
||||
#### Using `ThemeContext` directly |
||||
|
||||
```ts |
||||
import { ThemeContext } from '@grafana/ui'; |
||||
|
||||
<ThemeContext.Consumer>{theme => <Foo theme={theme} />}</ThemeContext.Consumer>; |
||||
``` |
||||
|
||||
#### Using `withTheme` HOC |
||||
|
||||
With this method your component will be automatically wrapped in `ThemeContext.Consumer` and provided with current theme via `theme` prop. Component used with `withTheme` must implement `Themeable` interface. |
||||
|
||||
```ts |
||||
import { ThemeContext, Themeable } from '@grafana/ui'; |
||||
|
||||
interface FooProps extends Themeable {} |
||||
|
||||
const Foo: React.FunctionComponent<FooProps> = () => ... |
||||
|
||||
export default withTheme(Foo); |
||||
``` |
||||
|
||||
### Storybook |
||||
|
||||
All stories are wrapped with `ThemeContext.Provider` using global decorator. To render `Themeable` component that's not wrapped by `withTheme` HOC you either create a new component in your story: |
||||
|
||||
```tsx |
||||
// Foo.story.tsx |
||||
const FooWithTheme = withTheme(Foo); |
||||
|
||||
FooStories.add('Story' () => { |
||||
return <FooWithTheme /> |
||||
}); |
||||
``` |
||||
|
||||
or use `renderComponentWithTheme` helper: |
||||
|
||||
```tsx |
||||
// Bar.story.tsx |
||||
|
||||
BarStories.add('Story' () => { |
||||
return renderComponentWithTheme(Bar, /* pass props here */) |
||||
}); |
||||
``` |
||||
|
||||
### Angular |
||||
|
||||
There should be very few cases when theme would be used in Angular context. For this purpise there is a function available that retrieves current theme: `import { getCurrentTheme } from app/core/utils/ConfigProvider` |
||||
|
||||
## Limitations |
||||
|
||||
- #### Hot updates |
||||
Changes in JS theme files _are not subject of hot updates_ during development. This applies to styles that comes from SASS files (which means 100% until we introduce css in js approach). This is a consequence of the fact that `getThemeVariable` util is executed during webpack pipeline. |
||||
- #### You must ensure ThemeContext provider is available in a React tree |
||||
By default all react2angular directives have `ThemeContext.Provider` ensured. But, there are cases where we create another React tree via `ReactDOM.render`. This happens in case of graph legend rendering and `ReactContainer` directive. In such cases theme consumption will fail. To make sure theme context is available in such cases, you need to wrap your rendered component with ThemeContext.Provider using `provideTheme` function: |
||||
|
||||
```typescript |
||||
// graph.ts |
||||
import { provideTheme } from 'app/core/utils/ConfigProvider'; |
||||
|
||||
// Create component with ThemeContext.Provider first. |
||||
// Otherwise React will create new components every time it renders! |
||||
const LegendWithThemeProvider = provideTheme(Legend); |
||||
|
||||
const legendReactElem = React.createElement(LegendWithThemeProvider, legendProps); |
||||
ReactDOM.render(legendReactElem, this.legendElem, () => this.renderPanel()); |
||||
``` |
||||
|
||||
`provideTheme` makes current theme available via ThemeContext by checking if user has `lightTheme` set in her boot data. |
Loading…
Reference in new issue