-
Notifications
You must be signed in to change notification settings - Fork 380
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1206 from fabiofranzini/enhanced-theme-provider
Added 'EnhancedThemeProvider' control
- Loading branch information
Showing
17 changed files
with
436 additions
and
1 deletion.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
109 changes: 109 additions & 0 deletions
109
docs/documentation/docs/controls/EnhancedThemeProvider.md
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,109 @@ | ||
# Enhanced Theme Provider | ||
|
||
The reasons behind this control are many and concern the use of Fluent UI controls currently officially supported by SPFx, that is: | ||
- `Problems with Teams theme support`, when hosting a Web Part like Tab or Personal App and specifically the lack of support by this version of Fluent UI React of the high contrast theme. | ||
- `Lack of basic style`, such as fonts, for basic HTML elements when creating `Web Parts hosted in Teams as Tabs or personal App`. | ||
- Lack of basic style, such as fonts, for basic HTML elements when creating `Web Parts in "isDomainIsolated" mode`, aka the Isolated Web Parts. | ||
|
||
Therefore, the control is to be considered as a sort of `wrapper for all react and non-react controls` that you want to add to the WebPart. | ||
|
||
The control `extends the functionality of the Fluent UI ThemeProvider control` (currently in version 7) by adding some logic thanks to the information contained in the 'context' property, that is: | ||
- If the Web Part is hosted inside SharePoint, the theme passed through the 'Theme' property will be used or the default one of the current site will be taken. | ||
- If the web part is hosted within Teams, the "Theme" property will be ignored and using the "Context" property checks which theme is currently applied and adds a handler to notify when the theme is changed. This allows you to manage the change of theme in Teams in real-time, without having to reload the Tab or the Personal App. | ||
|
||
Example of use in SharePoint in a `SharePointFullPage - Isolated web parts` (note that the titles H1, H2, H3 and the paragraph are normal html tags that automatically take the font and color style from the control): | ||
![Enhanced Theme Provider - SharePointFullPage - Isolated web parts](../assets/EnhancedThemeProviderSharePoint.gif) | ||
|
||
As for Teams, given the inconsistency of the theme system of Fluent UI NorthStar (used in Teams) and Fluent UI React (used by SPFx), the themes are "emulated". | ||
|
||
The control contains the refining of Teams' `Default`, `Dark` and `Hight Contrast` themes. | ||
|
||
The `Default` and `Dark themes` were created simply using the Fluent UI Themes designer and the primary colors of their corresponding Teams themes. | ||
|
||
For the `Hight Contrast` theme, on the other hand, given the complexity of creating a completely new theme and above all in Hight Contrast mode (neither supported nor gives SharePoint nor gives Fluent UI v7), it was decided to create the theme by hand and support only "main controls". | ||
|
||
`This means that this theme is not perfect and above all not all controls will be displayed correctly.` | ||
|
||
This is not a big deal, as the same theme provided by SharePoint has the same problems, it does not support Hight Contrast rendering for all controls. | ||
|
||
For the `Hight Contrast` theme (in Teams), only these controls are supported by this control: `ChoiceGroup, Checkbox, ComboBox, DatePicker, SpinButton, TextField, Toggle, PrimaryButton, DefaultButton, CompoundButton, IconButton`, other fluent controls may have color rendering problems. | ||
|
||
Example of use in Teams as a `TeamsPersonalApp` (note that the titles H1, H2, H3 and the paragraph are normal html tags that automatically take the font and color style from the control): | ||
![Enhanced Theme Provider - TeamsPersonalApp / TeamsTab](../assets/EnhancedThemeProviderTeams.gif) | ||
|
||
## How to use this control in your solutions | ||
|
||
- Check that you installed the `@pnp/spfx-controls-react` dependency. Check out the [getting started](../../#getting-started) page for more information about installing the dependency. | ||
- In your component file, import the `EnhancedThemeProvider` control as follows: | ||
|
||
```TypeScript | ||
import { EnhancedThemeProvider, getDefaultTheme, useTheme, ThemeContext } from "@pnp/spfx-controls-react/lib/EnhancedThemeProvider"; | ||
``` | ||
|
||
- Example on use the `EnhancedThemeProvider` control with only required properties: | ||
|
||
```TypeScript | ||
<EnhancedThemeProvider context={this.props.context}> | ||
{/* controls to apply the theme to */} | ||
</EnhancedThemeProvider> | ||
``` | ||
|
||
- Example on use the `EnhancedThemeProvider` control with the most important properties: | ||
|
||
```TypeScript | ||
<EnhancedThemeProvider applyTo="element" context={this.props.context} theme={this.props.themeVariant}> | ||
{/* controls to apply the theme to */} | ||
</EnhancedThemeProvider> | ||
``` | ||
|
||
The control provides the passage and/or creation of the theme according to what has been said before. | ||
In order to access the theme, from child controls, there are two modes, one for function-based controls, one for class-based controls. | ||
|
||
- Access the theme from the child control using a function component: | ||
```TypeScript | ||
export const ChildFunctionComponent = () => { | ||
const theme = useTheme(); | ||
|
||
return ( | ||
<DefaultButton theme={theme}>Example Child Control</DefaultButton> | ||
); | ||
} | ||
``` | ||
|
||
- Access the theme from the child control using a class component: | ||
```TypeScript | ||
export class ChildClassComponent extends React.Component { | ||
public render() { | ||
return ( | ||
<ThemeContext.Consumer> | ||
{theme => | ||
<DefaultButton theme={theme}>Example Child Control</DefaultButton> | ||
} | ||
</ThemeContext.Consumer> | ||
) | ||
} | ||
}; | ||
``` | ||
|
||
- Usage example using theme in child controls: | ||
```TypeScript | ||
<EnhancedThemeProvider applyTo="element" context={this.props.context} theme={this.props.themeVariant}> | ||
<ChildFunctionComponent /> | ||
<ChildClassComponent /> | ||
</EnhancedThemeProvider> | ||
``` | ||
|
||
## Implementation | ||
|
||
The `EnhancedThemeProvider` control can be configured with the following properties: | ||
|
||
| Property | Type | Required | Description | | ||
| ---- | ---- | ---- | ---- | | ||
| context | WebPartContext | yes | Set the context from the SPFx Web Part. | | ||
| as | React.ElementType | no | A component that should be used as the root element of the ThemeProvider component. | | ||
| ref | React.Ref<HTMLElement> | no | Optional ref to the root element. | | ||
| theme | PartialTheme \| Theme | no | Defines the theme provided by the user. | | ||
| renderer | StyleRenderer | no | Optional interface for registering dynamic styles. Defaults to using `merge-styles`. Use this to opt into a particular rendering implementation, such as `emotion`, `styled-components`, or `jss`. Note: performance will differ between all renders. Please measure your scenarios before using an alternative implementation. | | ||
| applyTo | 'element' \| 'body' \| 'none' | no | Defines where body-related theme is applied to. Setting to 'element' will apply body styles to the root element of ThemeProvider. Setting to 'body' will apply body styles to document body. Setting to 'none' will not apply body styles to either element or body.| | ||
|
||
![](https://telemetry.sharepointpnp.com/sp-dev-fx-controls-react/wiki/controls/EnhancedThemeProvider) |
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 |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './controls/EnhancedThemeProvider/index'; |
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,15 @@ | ||
import { createTheme, getTheme, ITheme } from "office-ui-fabric-react/lib/Styling"; | ||
|
||
export const fluentUIDefaultTheme = (): ITheme => { | ||
let currentTheme; | ||
const themeColorsFromWindow: any = (window as any).__themeState__.theme; | ||
if (themeColorsFromWindow) { | ||
currentTheme = createTheme({ | ||
palette: themeColorsFromWindow | ||
}); | ||
} | ||
else | ||
currentTheme = getTheme(); | ||
|
||
return currentTheme; | ||
}; |
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,29 @@ | ||
import { createTheme } from "office-ui-fabric-react/lib/Styling"; | ||
|
||
export const fluentUITeamsDarkTheme = createTheme({ | ||
palette: { | ||
themePrimary: "#7f85f5", | ||
themeLighterAlt: "#05050a", | ||
themeLighter: "#141527", | ||
themeLight: "#262849", | ||
themeTertiary: "#4c5093", | ||
themeSecondary: "#7075d7", | ||
themeDarkAlt: "#8c91f6", | ||
themeDark: "#9da2f7", | ||
themeDarker: "#b6baf9", | ||
neutralLighterAlt: "#282828", | ||
neutralLighter: "#313131", | ||
neutralLight: "#3f3f3f", | ||
neutralQuaternaryAlt: "#484848", | ||
neutralQuaternary: "#4f4f4f", | ||
neutralTertiaryAlt: "#6d6d6d", | ||
neutralTertiary: "#c8c8c8", | ||
neutralSecondary: "#d0d0d0", | ||
neutralPrimaryAlt: "#dadada", | ||
neutralPrimary: "#ffff", | ||
neutralDark: "#f4f4f4", | ||
black: "#ffffff", | ||
white: "#1f1f1f" | ||
}, | ||
isInverted: true | ||
}); |
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,29 @@ | ||
import { createTheme } from "office-ui-fabric-react/lib/Styling"; | ||
|
||
export const fluentUITeamsDefaultTheme = createTheme({ | ||
palette: { | ||
themePrimary: "#6264A7", | ||
themeLighterAlt: "#f7f7fb", | ||
themeLighter: "#e1e1f1", | ||
themeLight: "#c8c9e4", | ||
themeTertiary: "#989ac9", | ||
themeSecondary: "#7173b0", | ||
themeDarkAlt: "#585a95", | ||
themeDark: "#4a4c7e", | ||
themeDarker: "#37385d", | ||
neutralLighterAlt: "#eeeeee", | ||
neutralLighter: "#eaeaea", | ||
neutralLight: "#e1e1e1", | ||
neutralQuaternaryAlt: "#d1d1d1", | ||
neutralQuaternary: "#c8c8c8", | ||
neutralTertiaryAlt: "#c0c0c0", | ||
neutralTertiary: "#acacac", | ||
neutralSecondary: "#919191", | ||
neutralPrimaryAlt: "#767676", | ||
neutralPrimary: "#0b0b0b", | ||
neutralDark: "#404040", | ||
black: "#252525", | ||
white: "#F5F5F5" | ||
}, | ||
isInverted: false | ||
}); |
37 changes: 37 additions & 0 deletions
37
src/common/fluentUIThemes/FluentUITeamsHighContrastTheme.ts
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,37 @@ | ||
import { createTheme } from "office-ui-fabric-react/lib/Styling"; | ||
|
||
export const fluentUITeamsHighContrastTheme = createTheme({ | ||
palette: { | ||
themePrimary: "#00ebff", | ||
themeLighterAlt: "#0a0a00", | ||
themeLighter: "#292900", | ||
themeLight: "#4d4d00", | ||
themeTertiary: "#999900", | ||
themeSecondary: "#e0e000", | ||
themeDarkAlt: "#ffff19", | ||
themeDark: "#ffff3d", | ||
themeDarker: "#ffff70", | ||
neutralLighterAlt: "#0b0b0b", | ||
neutralLighter: "#151515", | ||
neutralLight: "#252525", | ||
neutralQuaternaryAlt: "#2f2f2f", | ||
neutralQuaternary: "#373737", | ||
neutralTertiaryAlt: "#595959", | ||
neutralTertiary: "#fafafa", | ||
neutralSecondary: "#fbfbfb", | ||
neutralPrimaryAlt: "#fcfcfc", | ||
neutralPrimary: "#f8f8f8", | ||
neutralDark: "#fdfdfd", | ||
black: "#fefefe", | ||
white: "#000000" | ||
}, | ||
isInverted: true, | ||
semanticColors: { | ||
buttonBackgroundDisabled: "#3ff23f", | ||
buttonTextDisabled: "#000000", | ||
primaryButtonBackgroundDisabled: "#3ff23f", | ||
primaryButtonTextDisabled: "#000000", | ||
link: "#ffff00", | ||
linkHovered: "#ffff00" | ||
} | ||
}); |
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,4 @@ | ||
export * from './FluentUIDefaultTheme'; | ||
export * from './FluentUITeamsDarkTheme'; | ||
export * from './FluentUITeamsDefaultTheme'; | ||
export * from './FluentUITeamsHighContrastTheme'; |
91 changes: 91 additions & 0 deletions
91
src/controls/EnhancedThemeProvider/EnhancedThemeProvider.tsx
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,91 @@ | ||
import { ThemeProvider } from '@fluentui/react-theme-provider'; | ||
import { getVariant, VariantThemeType } from "@fluentui/scheme-utilities"; | ||
import { initializeIcons } from 'office-ui-fabric-react/lib/Icons'; | ||
import { createTheme, getTheme, ITheme } from "office-ui-fabric-react/lib/Styling"; | ||
import * as React from "react"; | ||
import { useCallback, useEffect, useState } from 'react'; | ||
import { fluentUITeamsDarkTheme } from '../../common/fluentUIThemes/FluentUITeamsDarkTheme'; | ||
import { fluentUITeamsDefaultTheme } from '../../common/fluentUIThemes/FluentUITeamsDefaultTheme'; | ||
import { fluentUITeamsHighContrastTheme } from '../../common/fluentUIThemes/FluentUITeamsHighContrastTheme'; | ||
import { IEnhancedThemeProviderProps } from './IEnhancedThemeProviderProps'; | ||
import { ThemeContext, useTheme } from '@fluentui/react-theme-provider'; | ||
import * as telemetry from '../../common/telemetry'; | ||
|
||
const getDefaultTheme = (): ITheme => { | ||
let currentTheme; | ||
const themeColorsFromWindow: any = (window as any)?.__themeState__?.theme; | ||
if (themeColorsFromWindow) { | ||
currentTheme = createTheme({ | ||
palette: themeColorsFromWindow | ||
}); | ||
} | ||
else { | ||
currentTheme = getTheme(); | ||
} | ||
|
||
return currentTheme; | ||
}; | ||
|
||
const EnhancedThemeProvider = (props: IEnhancedThemeProviderProps) => { | ||
|
||
const [isInTeams, setIsInTeams] = useState(false); | ||
const [teamsThemeName, setTeamsThemeName] = useState<string>(null); | ||
|
||
// track the telemetry as 'ReactEnhancedThemeProvider' | ||
useEffect(() => { | ||
telemetry.track('ReactEnhancedThemeProvider'); | ||
}, []); | ||
// ***** | ||
|
||
useEffect(() => { | ||
initializeIcons(); | ||
}, []); | ||
|
||
useEffect(() => { | ||
setIsInTeams((props.context.sdks.microsoftTeams) ? true : false); | ||
}, [props.context]); | ||
|
||
useEffect(() => { | ||
if (isInTeams) { | ||
setTeamsThemeName(props.context.sdks.microsoftTeams?.context?.theme); | ||
props.context.sdks?.microsoftTeams?.teamsJs?.registerOnThemeChangeHandler((theme: string) => { | ||
setTeamsThemeName(theme); | ||
}); | ||
} | ||
}, [props.context, isInTeams]); | ||
|
||
const themeToApply = useCallback( | ||
() => { | ||
let workingTheme: ITheme; | ||
|
||
if (isInTeams) { | ||
switch (teamsThemeName) { | ||
case "default": workingTheme = fluentUITeamsDefaultTheme; | ||
break; | ||
case "dark": workingTheme = fluentUITeamsDarkTheme; | ||
break; | ||
case "contrast": workingTheme = fluentUITeamsHighContrastTheme; | ||
break; | ||
default: workingTheme = fluentUITeamsDefaultTheme; | ||
break; | ||
} | ||
} else if (props.theme) { | ||
workingTheme = getVariant(props.theme, VariantThemeType.None); | ||
} else { | ||
workingTheme = getDefaultTheme(); | ||
} | ||
|
||
return workingTheme; | ||
}, | ||
[props.theme, teamsThemeName]); | ||
|
||
return ( | ||
<ThemeProvider | ||
{...props} | ||
theme={themeToApply()}> | ||
{props.children} | ||
</ThemeProvider> | ||
); | ||
}; | ||
|
||
export { EnhancedThemeProvider, getDefaultTheme, useTheme, ThemeContext }; |
9 changes: 9 additions & 0 deletions
9
src/controls/EnhancedThemeProvider/IEnhancedThemeProviderProps.ts
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,9 @@ | ||
import { ThemeProviderProps } from '@fluentui/react-theme-provider'; | ||
import { WebPartContext } from '@microsoft/sp-webpart-base'; | ||
|
||
export interface IEnhancedThemeProviderProps extends ThemeProviderProps { | ||
/** | ||
* Set the context from the SPFx component. | ||
*/ | ||
context: WebPartContext; | ||
} |
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,2 @@ | ||
export * from './EnhancedThemeProvider'; | ||
export * from './IEnhancedThemeProviderProps'; |
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
Oops, something went wrong.