Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/fabric-website-resources/webpack.serve.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ module.exports = resources.createServeConfig({

resolve: {
alias: {
'office-ui-fabric-react$': path.resolve(__dirname, '../../packages/office-ui-fabric-react/src'),
'office-ui-fabric-react/src': path.resolve(__dirname, '../../packages/office-ui-fabric-react/src'),
'office-ui-fabric-react/lib': path.resolve(__dirname, '../../packages/office-ui-fabric-react/src'),
'Props.ts.js': 'Props',
Expand Down
2 changes: 1 addition & 1 deletion apps/todo-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"office-ui-fabric-react": ">=6.40.0 <7.0.0",
"react": ">=16.3.2-0 <17.0.0",
"react-dom": ">=16.3.2-0 <17.0.0",
"typescript": "2.8.4",
"typescript": "3.0.0-rc",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

"tslib": "^1.7.1"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"packageName": "@uifabric/fabric-website-resources",
"comment": "Updating serve config to respect oufr imports.",
"type": "patch"
}
],
"packageName": "@uifabric/fabric-website-resources",
"email": "dzearing@microsoft.com"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"packageName": "@uifabric/utilities",
"comment": "Customizer: moving to use React 16 context.",
"type": "minor"
}
],
"packageName": "@uifabric/utilities",
"email": "dzearing@microsoft.com"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"packageName": "office-ui-fabric-react",
"comment": "Check: adjusting shouldComponentUpdate to not ignore theme changes.",
"type": "patch"
}
],
"packageName": "office-ui-fabric-react",
"email": "dzearing@microsoft.com"
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ export class CheckBase extends BaseComponent<ICheckProps, {}> {
};

public shouldComponentUpdate(newProps: ICheckProps): boolean {
return this.props.checked !== newProps.checked;
return (
this.props.className !== newProps.className ||
this.props.checked !== newProps.checked ||
this.props.theme !== newProps.theme
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if styles prop changes? I feel like having shouldComponentUpdate for such a simple component is code smell and we're checking most of the props now. I'm curious why it exists at all

}

public render(): JSX.Element {
Expand Down
59 changes: 28 additions & 31 deletions packages/utilities/src/Customizer.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import * as React from 'react';
import * as PropTypes from 'prop-types';
import { BaseComponent, IBaseProps } from './BaseComponent';
import { ICustomizations, Settings, SettingsFunction } from './Customizations';
import { Customizations, ICustomizations, Settings, SettingsFunction } from './Customizations';

export interface ICustomizerContext {
customizations: ICustomizations;
}

export const CustomizerContext = React.createContext<ICustomizerContext>({
customizations: {
settings: {},
scopedSettings: {}
}
});

export type ICustomizerProps = IBaseProps &
Partial<{
/**
Expand Down Expand Up @@ -54,7 +60,8 @@ export type ICustomizerProps = IBaseProps &

/**
* The Customizer component allows for default props to be mixed into components which
* are decorated with the customizable() decorator. This enables injection scenarios like:
* are decorated with the customizable() decorator, or use the styled HOC. This enables
* injection scenarios like:
*
* 1. render svg icons instead of the icon font within all buttons
* 2. inject a custom theme object into a component
Expand All @@ -65,40 +72,30 @@ export type ICustomizerProps = IBaseProps &
*
* @public
*/
export class Customizer extends BaseComponent<ICustomizerProps, ICustomizerContext> {
public static contextTypes: {
customizations: PropTypes.Requireable<{}>;
} = {
customizations: PropTypes.object
};

public static childContextTypes: {
customizations: PropTypes.Requireable<{}>;
} =
Customizer.contextTypes;

// tslint:disable-next-line:no-any
constructor(props: ICustomizerProps, context: any) {
super(props);

this.state = this._getCustomizations(props, context);
export class Customizer extends BaseComponent<ICustomizerProps> {
public componentDidMount(): void {
Customizations.observe(this._onCustomizationChange);
}

public getChildContext(): ICustomizerContext {
return this.state;
}

// tslint:disable-next-line:no-any
public componentWillReceiveProps(newProps: any, newContext: any): void {
this.setState(this._getCustomizations(newProps, newContext));
public componentWillUnmount(): void {
Customizations.unobserve(this._onCustomizationChange);
}

public render(): React.ReactElement<{}> {
return React.Children.only(this.props.children);
return (
<CustomizerContext.Consumer>
{(parentContext: ICustomizerContext) => {
const newContext = this._getCustomizations(this.props, parentContext);

return <CustomizerContext.Provider value={newContext}>{this.props.children}</CustomizerContext.Provider>;
}}
</CustomizerContext.Consumer>
);
}

private _getCustomizations(props: ICustomizerProps, context: ICustomizerContext): ICustomizerContext {
const { customizations = { settings: {}, scopedSettings: {} } } = context;
private _onCustomizationChange = () => this.forceUpdate();

private _getCustomizations(props: ICustomizerProps, parentContext: ICustomizerContext): ICustomizerContext {
const { customizations = { settings: {}, scopedSettings: {} } } = parentContext || {};

return {
customizations: {
Expand Down
23 changes: 10 additions & 13 deletions packages/utilities/src/customizable.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import * as React from 'react';
import * as PropTypes from 'prop-types';
import { Customizations } from './Customizations';
import { hoistStatics } from './hoistStatics';

export const CustomizableContextTypes = {
customizations: PropTypes.object
};
import { CustomizerContext, ICustomizerContext } from './Customizer';

export function customizable(
scope: string,
Expand All @@ -21,11 +17,9 @@ export function customizable(
const resultClass = class ComponentWithInjectedProps extends React.Component<P, {}> {
public static displayName: string = 'Customized' + scope;

public static contextTypes = CustomizableContextTypes;

// tslint:disable-next-line:no-any
constructor(props: P, context: any) {
super(props, context);
constructor(props: P) {
super(props);

this._onSettingChanged = this._onSettingChanged.bind(this);
}
Expand All @@ -39,11 +33,14 @@ export function customizable(
}

public render(): JSX.Element {
const defaultProps = Customizations.getSettings(fields, scope, this.context.customizations);

return (
// tslint:disable-next-line:no-any
<ComposedComponent {...defaultProps} {...this.props as any} />
<CustomizerContext.Consumer>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 I can steal this for createComponent 😄

{(context: ICustomizerContext) => {
const defaultProps = Customizations.getSettings(fields, scope, context.customizations);
// tslint:disable-next-line:no-any
return <ComposedComponent {...defaultProps} {...this.props as any} />;
}}
</CustomizerContext.Consumer>
);
}

Expand Down
29 changes: 15 additions & 14 deletions packages/utilities/src/styled.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import * as React from 'react';
import { concatStyleSets, IStyleSet, IStyleFunctionOrObject, IConcatenatedStyleSet } from '@uifabric/merge-styles';
import { IStyleFunction } from './IStyleFunction';
import { CustomizableContextTypes } from './customizable';
import { Customizations, ICustomizations } from './Customizations';
import { Customizations } from './Customizations';
import { CustomizerContext, ICustomizerContext } from './Customizer';

export interface IPropsWithStyles<TStyleProps, TStyleSet extends IStyleSet<TStyleSet>> {
styles?: IStyleFunctionOrObject<TStyleProps, TStyleSet>;
Expand Down Expand Up @@ -49,24 +48,26 @@ export function styled<
getProps?: (props: TComponentProps) => Partial<TComponentProps>,
customizable?: ICustomizableProps
): (props: TComponentProps) => JSX.Element {
const Wrapped: React.StatelessComponent<TComponentProps> = (
componentProps: TComponentProps,
context: { customizations: ICustomizations }
) => {
const Wrapped: React.StatelessComponent<TComponentProps> = (componentProps: TComponentProps) => {
customizable = customizable || { scope: '', fields: undefined };

const { scope, fields = DefaultFields } = customizable;
const settings = Customizations.getSettings(fields, scope, context.customizations);
const { styles: customizedStyles, ...rest } = settings;
const styles = (styleProps: TStyleProps) =>
_resolve(styleProps, baseStyles, customizedStyles, componentProps.styles);

const additionalProps = getProps ? getProps(componentProps) : undefined;
return (
<CustomizerContext.Consumer>
{(context: ICustomizerContext) => {
const settings = Customizations.getSettings(fields, scope, context.customizations);
const { styles: customizedStyles, ...rest } = settings;
const styles = (styleProps: TStyleProps) =>
_resolve(styleProps, baseStyles, customizedStyles, componentProps.styles);

return <Component {...rest} {...additionalProps} {...componentProps} styles={styles} />;
const additionalProps = getProps ? getProps(componentProps) : undefined;
return <Component {...rest} {...additionalProps} {...componentProps} styles={styles} />;
}}
</CustomizerContext.Consumer>
);
};

Wrapped.contextTypes = CustomizableContextTypes;
Wrapped.displayName = `Styled${Component.displayName || Component.name}`;

return Wrapped as (props: TComponentProps) => JSX.Element;
Expand Down