Skip to content

Commit

Permalink
feat: add support for nonce in Fela renderer (#25992)
Browse files Browse the repository at this point in the history
  • Loading branch information
layershifter authored Dec 20, 2022
1 parent 9231548 commit 3ec3c77
Show file tree
Hide file tree
Showing 9 changed files with 176 additions and 108 deletions.
6 changes: 6 additions & 0 deletions packages/fluentui/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,17 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm

## [Unreleased]

### BREAKING CHANGES
- `createFelaRenderer` is now a function @layershifter ([#25992](https://github.com/microsoft/fluentui/pull/25992))

### Fixes
- `Carousel` fix for FZ - Adding data-is-visible prop #25973 @kolaps33 ([#25973](https://github.com/microsoft/fluentui/pull/25973))
- Fix `Dropdown` allowing interaction with selected items when disabled @chpalac ([#25954](https://github.com/microsoft/fluentui/pull/25954))
- Fix `Button` styles for `disabledFocusable` when `text` @chpalac ([#26012](https://github.com/microsoft/fluentui/pull/26012))

### Features
- `nonce` can be configured for Fela renderer @layershifter ([#25992](https://github.com/microsoft/fluentui/pull/25992))

<!--------------------------------[ v0.65.0 ]------------------------------- -->
## [v0.65.0](https://github.com/microsoft/fluentui/tree/@fluentui/react-northstar_v0.65.0) (2022-10-31)
[Compare changes](https://github.com/microsoft/fluentui/compare/@fluentui/react-northstar_v0.64.0..@fluentui/react-northstar_v0.65.0)
Expand Down
2 changes: 1 addition & 1 deletion packages/fluentui/docs/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const themes = {
};

function useRendererFactory(): CreateRenderer {
const rendererFactory = localStorage.fluentRenderer === 'emotion' ? createEmotionRenderer() : createFelaRenderer;
const rendererFactory = localStorage.fluentRenderer === 'emotion' ? createEmotionRenderer() : createFelaRenderer();

React.useEffect(() => {
(window as any).setFluentRenderer = (rendererName: 'fela' | 'emotion') => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ import { createFelaRenderer } from '@fluentui/react-northstar-fela-renderer';
import { CreateRenderer } from '@fluentui/react-northstar-styles-renderer';
import * as React from 'react';

export const RendererContext = React.createContext<CreateRenderer>(createFelaRenderer);
export const RendererContext = React.createContext<CreateRenderer>(createFelaRenderer());
7 changes: 4 additions & 3 deletions packages/fluentui/react-northstar-fela-renderer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,22 @@
"@fluentui/styles": "^0.65.0",
"css-in-js-utils": "^3.0.0",
"fela": "^10.6.1",
"fela-dom": "^11.7.0",
"fela-plugin-embedded": "^10.6.1",
"fela-plugin-fallback-value": "^10.6.1",
"fela-plugin-placeholder-prefixer": "^10.6.1",
"fela-plugin-rtl": "^10.6.1",
"fela-tools": "^10.6.1",
"fela-utils": "^10.6.1",
"fela-utils": "^11.7.0",
"inline-style-expand-shorthand": "^1.2.0",
"lodash": "^4.17.15",
"react-fela": "^10.6.1",
"stylis": "^3.5.4"
},
"devDependencies": {
"@fluentui/eslint-plugin": "*",
"@fluentui/scripts": "^1.0.0",
"lerna-alias": "^3.0.3-0"
"lerna-alias": "^3.0.3-0",
"react-fela": "^10.6.1"
},
"files": [
"dist"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { render, rehydrate } from 'fela-dom';
import * as React from 'react';

import { FelaRenderer } from './types';

// Copied from https://github.com/robinweser/fela/blob/master/packages/fela-bindings/src/RendererProviderFactory.js

function hasDOM(renderer: FelaRenderer, targetDocument: Document | undefined) {
// ensure we're on a browser by using document since window is defined in e.g. React Native
// see https://github.com/robinweser/fela/issues/736
if (typeof document === 'undefined') {
return false;
}

const doc = targetDocument || document;

return renderer && doc && doc.createElement;
}

function hasServerRenderedStyle(targetDocument = document) {
return targetDocument.querySelectorAll('[data-fela-type]').length > 0;
}

type RendererProviderProps = {
renderer: FelaRenderer;
rehydrate?: boolean;
targetDocument?: Document;
};

// Typings provided by "fela" package are outdated, this overrides provides proper definition
declare module 'fela-dom' {
function render(renderer: FelaRenderer, targetDocument?: Document): void;
function rehydrate(renderer: FelaRenderer, targetDocument?: Document): void;
}

export class RendererProvider extends React.Component<RendererProviderProps, Record<string, unknown>> {
constructor(props: RendererProviderProps) {
super(props);
this._renderStyle();
}

componentDidUpdate(prevProps: RendererProviderProps) {
if (prevProps.renderer !== this.props.renderer) {
// add warning that renderer is changed
this._renderStyle();
}
}

_renderStyle() {
const { renderer, rehydrate: shouldRehydrate, targetDocument } = this.props;

if (hasDOM(renderer, targetDocument)) {
if (shouldRehydrate && hasServerRenderedStyle(targetDocument)) {
rehydrate(renderer, targetDocument);
} else {
render(renderer, targetDocument);
}
}
}

render() {
return <>{this.props.children}</>;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import felaPluginFallbackValue from 'fela-plugin-fallback-value';
import felaPluginPlaceholderPrefixer from 'fela-plugin-placeholder-prefixer';
import felaPluginRtl from 'fela-plugin-rtl';
import * as React from 'react';
import { RendererProvider } from 'react-fela';

import { felaDisableAnimationsPlugin } from './felaDisableAnimationsPlugin';
import { felaExpandCssShorthandsPlugin } from './felaExpandCssShorthandsPlugin';
Expand All @@ -14,7 +13,8 @@ import { felaInvokeKeyframesPlugin } from './felaInvokeKeyframesPlugin';
import { felaPerformanceEnhancer } from './felaPerformanceEnhancer';
import { felaSanitizeCssPlugin } from './felaSanitizeCssPlugin';
import { felaStylisEnhancer } from './felaStylisEnhancer';
import { FelaRendererParam } from './types';
import { RendererProvider } from './RendererProvider';
import { FelaRenderer, FelaRendererParam } from './types';

let felaDevMode = false;

Expand Down Expand Up @@ -92,58 +92,72 @@ const rendererConfig = {
],
};

export const createFelaRenderer: CreateRenderer = () => {
const felaRenderer = createRenderer(rendererConfig) as IRenderer & {
listeners: [];
nodes: Record<string, HTMLStyleElement>;
updateSubscription: Function | undefined;
};
let usedRenderers: number = 0;

// rehydration disabled to avoid leaking styles between renderers
// https://github.com/rofrischmann/fela/blob/master/docs/api/fela-dom/rehydrate.md
const Provider: Renderer['Provider'] = props => (
<RendererProvider renderer={felaRenderer} {...{ rehydrate: false, targetDocument: props.target }}>
{props.children}
</RendererProvider>
);

return {
registerUsage: () => {
usedRenderers += 1;
},
unregisterUsage: () => {
usedRenderers -= 1;

if (usedRenderers === 0) {
felaRenderer.listeners = [];
felaRenderer.nodes = {};
felaRenderer.updateSubscription = undefined;
}
},

renderFont: font => {
felaRenderer.renderFont(font.name, font.paths, font.props);
},
renderGlobal: felaRenderer.renderStatic,
renderRule: (styles, param) => {
const felaParam: FelaRendererParam = {
...param,
theme: { direction: param.direction },
};
export type CreateFelaRendererOptions = {
nonce?: string;
};

return felaRenderer.renderRule(() => (styles as unknown) as IStyle, felaParam);
},
export function createFelaRenderer(options: CreateFelaRendererOptions = {}): CreateRenderer {
const { nonce } = options;

// getOriginalRenderer() is implemented only for tests to be compatible with jest-react-fela expectations.
getOriginalRenderer: (): IRenderer => {
if (process.env.NODE_ENV !== 'test') {
throw new Error('This method implements private API and can be used only in tests');
}
return () => {
const felaRenderer = createRenderer(rendererConfig) as FelaRenderer & {
listeners: [];
nodes: Record<string, HTMLStyleElement>;
updateSubscription: Function | undefined;
};
let usedRenderers: number = 0;

return felaRenderer;
},
if (nonce) {
felaRenderer.styleNodeAttributes = {
nonce,
};
}

// rehydration disabled to avoid leaking styles between renderers
// https://github.com/rofrischmann/fela/blob/master/docs/api/fela-dom/rehydrate.md
const Provider: Renderer['Provider'] = props => (
<RendererProvider renderer={felaRenderer} rehydrate={false} targetDocument={props.target}>
{props.children}
</RendererProvider>
);

Provider,
return {
registerUsage: () => {
usedRenderers += 1;
},
unregisterUsage: () => {
usedRenderers -= 1;

if (usedRenderers === 0) {
felaRenderer.listeners = [];
felaRenderer.nodes = {};
felaRenderer.updateSubscription = undefined;
}
},

renderFont: font => {
felaRenderer.renderFont(font.name, font.paths, font.props);
},
renderGlobal: felaRenderer.renderStatic,
renderRule: (styles, param) => {
const felaParam: FelaRendererParam = {
...param,
theme: { direction: param.direction },
};

return felaRenderer.renderRule(() => (styles as unknown) as IStyle, felaParam);
},

// getOriginalRenderer() is implemented only for tests to be compatible with jest-react-fela expectations.
getOriginalRenderer: (): IRenderer => {
if (process.env.NODE_ENV !== 'test') {
throw new Error('This method implements private API and can be used only in tests');
}

return felaRenderer;
},

Provider,
};
};
};
}
2 changes: 2 additions & 0 deletions packages/fluentui/react-northstar-fela-renderer/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ export type FelaRenderer = IRenderer & {
media?: string,
support?: string,
): string;

styleNodeAttributes: Record<string, string | number | boolean>;
};

export type FelaRendererChange = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { FelaComponent, RendererProvider, ThemeProvider } from 'react-fela';

const felaRenderer = (createFelaRenderer() as any).getOriginalRenderer();
const felaRenderer = (createFelaRenderer()() as any).getOriginalRenderer();

function createSnapshot(component: JSX.Element, theme = {}) {
const div = document.createElement('div');
Expand Down
Loading

0 comments on commit 3ec3c77

Please sign in to comment.