Bläddra i källkod

Docs: How to work with themes (#17876)

* Add docs for themes

* Edit description a little bit

* Update themes.md
Tobias Skarhed 6 år sedan
förälder
incheckning
498572d2d8
1 ändrade filer med 103 tillägg och 0 borttagningar
  1. 103 0
      style_guides/themes.md

+ 103 - 0
style_guides/themes.md

@@ -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.