Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[AppProvider] Update context #1424

Merged
merged 5 commits into from
May 15, 2019
Merged
Show file tree
Hide file tree
Changes from all 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 UNRELEASED-V4.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Use [the changelog guidelines](https://git.io/polaris-changelog-guidelines) to f

### Code quality

- Updated all our context files to export react context rather than a provider and consumer ([#1459](https://github.com/Shopify/polaris-react/pull/1459))
- Upgraded the `Autocomplete` component from legacy context API to use createContext ([#1403](https://github.com/Shopify/polaris-react/pull/1403))
- Updated `ThemeProvider` to use the new context api ([#1396](https://github.com/Shopify/polaris-react/pull/1396))
- Updated `AppProvider` to no longer use `componentWillReceiveProps`([#1255](https://github.com/Shopify/polaris-react/pull/1255))
Expand Down
47 changes: 13 additions & 34 deletions src/components/AppProvider/AppProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,11 @@ import {
ScrollLockManager,
createAppProviderContext,
} from './utilities';
import {
AppProviderProps,
Context,
polarisAppProviderContextTypes,
} from './types';
import AppProviderContext, {AppProviderContextType} from './context';
import {AppProviderProps} from './types';

interface State {
polaris: Context;
context: AppProviderContextType;
}

// The script in the styleguide that generates the Props Explorer data expects
Expand All @@ -22,10 +19,8 @@ interface State {
interface Props extends AppProviderProps {}

export default class AppProvider extends React.Component<Props, State> {
static childContextTypes = polarisAppProviderContextTypes;
private stickyManager: StickyManager;
private scrollLockManager: ScrollLockManager;
private subscriptions: {(): void}[] = [];

constructor(props: Props) {
super(props);
Expand All @@ -34,12 +29,10 @@ export default class AppProvider extends React.Component<Props, State> {
const {theme, children, ...rest} = this.props;

this.state = {
polaris: createAppProviderContext({
context: createAppProviderContext({
...rest,
stickyManager: this.stickyManager,
scrollLockManager: this.scrollLockManager,
subscribe: this.subscribe,
unsubscribe: this.unsubscribe,
}),
};
}
Expand Down Expand Up @@ -71,41 +64,27 @@ export default class AppProvider extends React.Component<Props, State> {

// eslint-disable-next-line react/no-did-update-set-state
this.setState({
polaris: createAppProviderContext({
context: createAppProviderContext({
i18n,
linkComponent,
apiKey,
shopOrigin,
forceRedirect,
stickyManager: this.stickyManager,
subscribe: this.subscribe,
unsubscribe: this.unsubscribe,
}),
});

this.subscriptions.forEach((subscriberCallback) => subscriberCallback());
}

getChildContext(): Context {
return this.state.polaris;
}

render() {
const {theme = {logo: null}} = this.props;
const {theme = {logo: null}, children} = this.props;
const {context} = this.state;

return (
<ThemeProvider theme={theme}>
{React.Children.only(this.props.children)}
</ThemeProvider>
<AppProviderContext.Provider value={context}>
<ThemeProvider theme={theme}>
{React.Children.only(children)}
</ThemeProvider>
</AppProviderContext.Provider>
);
}

subscribe = (callback: () => void) => {
this.subscriptions.push(callback);
};

unsubscribe = (callback: () => void) => {
this.subscriptions = this.subscriptions.filter(
(subscription) => subscription !== callback,
);
};
}
18 changes: 18 additions & 0 deletions src/components/AppProvider/context.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import * as React from 'react';
import {ClientApplication} from '@shopify/app-bridge';
import createPolarisContext from './utilities/createPolarisContext';
import {Intl, Link, StickyManager, ScrollLockManager} from './utilities';

export interface AppProviderContextType {
intl: Intl;
link: Link;
stickyManager: StickyManager;
scrollLockManager: ScrollLockManager;
appBridge?: ClientApplication<{}>;
}

const AppProviderContext = React.createContext<AppProviderContextType>(
createPolarisContext(),
);

export default AppProviderContext;
8 changes: 2 additions & 6 deletions src/components/AppProvider/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,12 @@ export {
withAppProvider,
createAppProviderContext,
createPolarisContext,
withSticky,
WithAppProviderProps,
TranslationDictionary,
PrimitiveReplacementDictionary,
ComplexReplacementDictionary,
CreateAppProviderContext,
} from './utilities';
export {
AppProviderProps as Props,
Context,
polarisAppProviderContextTypes,
} from './types';
export {AppProviderProps as Props} from './types';
export {default as AppProviderContext, AppProviderContextType} from './context';
export {default} from './AppProvider';
35 changes: 15 additions & 20 deletions src/components/AppProvider/tests/AppProvider.test.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,28 @@
import * as React from 'react';
import {mount} from 'enzyme';
import {polarisAppProviderContextTypes} from '../types';
import AppProviderContext from '../context';
import AppProvider from '../AppProvider';
import {mountWithAppProvider} from '../../../test-utilities';

describe('<AppProvider />', () => {
it('updates polaris context when props change', () => {
const CustomLinkComponent = () => {
return <a href="test">Custom Link Component</a>;
};

// eslint-disable-next-line react/prefer-stateless-function
class Child extends React.Component {
static contextTypes = polarisAppProviderContextTypes;

render() {
return <div />;
}
}
const Child: React.SFC<{}> = (_props) => (
<AppProviderContext.Consumer>
{({link: {linkComponent}}) =>
linkComponent ? <div id="child" /> : null
}
</AppProviderContext.Consumer>
);
const LinkComponent = () => <div />;

const wrapper = mount(
const wrapper = mountWithAppProvider(
<AppProvider>
<Child />
</AppProvider>,
);

wrapper.setProps({linkComponent: CustomLinkComponent});

expect(
wrapper.find(Child).instance().context.polaris.link.linkComponent,
).toBe(CustomLinkComponent);
expect(wrapper.find('#child')).toHaveLength(0);
wrapper.setProps({linkComponent: LinkComponent});
wrapper.update();
expect(wrapper.find('#child')).toHaveLength(1);
});
});
30 changes: 2 additions & 28 deletions src/components/AppProvider/types.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,6 @@
import {ClientApplication} from '@shopify/app-bridge';
import {ValidationMap} from 'react';
import * as PropTypes from 'prop-types';
import {LinkLikeComponent} from '../UnstyledLink';
import {Theme, THEME_CONTEXT_TYPES as polarisTheme} from '../ThemeProvider';
import {
Intl,
Link,
StickyManager,
ScrollLockManager,
TranslationDictionary,
} from './utilities';
import {Theme} from '../ThemeProvider';
import {TranslationDictionary} from './utilities';

export interface AppProviderProps {
/** A locale object or array of locale objects that overrides default translations */
Expand All @@ -29,20 +20,3 @@ export interface AppProviderProps {
/** Custom logos and colors provided to select components */
theme?: Theme;
}

export interface Context {
polaris: {
Copy link
Member Author

Choose a reason for hiding this comment

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

Removing the polaris namespace now since we consume it from a render prop rather than context types. We won't have to worry about collisions now

intl: Intl;
link: Link;
stickyManager: StickyManager;
scrollLockManager: ScrollLockManager;
subscribe?(callback: () => void): void;
unsubscribe?(callback: () => void): void;
appBridge?: ClientApplication<{}>;
};
}

export const polarisAppProviderContextTypes: ValidationMap<any> = {
polaris: PropTypes.any,
...polarisTheme,
};
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export interface StickyItem {
/** Placeholder element */
placeHolderNode: HTMLElement;
/** Element outlining the fixed position boundaries */
boundingElement: HTMLElement | null;
boundingElement?: HTMLElement | null;
Copy link
Member Author

Choose a reason for hiding this comment

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

There was a type issue and boundElement could be undefined

/** Offset vertical spacing from the top of the scrollable container */
offset: boolean;
/** Should the element remain in a fixed position when the layout is stacked (smaller screens) */
Expand Down
3 changes: 3 additions & 0 deletions src/components/AppProvider/utilities/StickyManager/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import StickyManager from './StickyManager';

export default StickyManager;
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import {noop} from '@shopify/javascript-utilities/other';
import createApp, {
getShopOrigin,
LifecycleHook,
DispatchActionHook,
} from '@shopify/app-bridge';
import {AppProviderProps, Context} from '../../types';
import {StickyManager} from '../withSticky';
import {AppProviderContextType} from '../../context';
import {AppProviderProps} from '../../types';
import StickyManager from '../StickyManager';
import ScrollLockManager from '../ScrollLockManager';
import Intl from '../Intl';
import Link from '../Link';
Expand All @@ -14,8 +14,6 @@ import {polarisVersion} from '../../../../configure';
export interface CreateAppProviderContext extends AppProviderProps {
stickyManager?: StickyManager;
scrollLockManager?: ScrollLockManager;
subscribe?(callback: () => void): void;
unsubscribe?(callback: () => void): void;
}

export default function createAppProviderContext({
Expand All @@ -26,9 +24,7 @@ export default function createAppProviderContext({
forceRedirect,
stickyManager,
scrollLockManager,
subscribe = noop,
unsubscribe = noop,
}: CreateAppProviderContext = {}): Context {
}: CreateAppProviderContext = {}): AppProviderContextType {
const intl = new Intl(i18n);
const link = new Link(linkComponent);
const appBridge = apiKey
Expand All @@ -44,15 +40,11 @@ export default function createAppProviderContext({
}

return {
polaris: {
intl,
link,
stickyManager: stickyManager || new StickyManager(),
scrollLockManager: scrollLockManager || new ScrollLockManager(),
subscribe,
unsubscribe,
appBridge,
},
intl,
link,
stickyManager: stickyManager || new StickyManager(),
scrollLockManager: scrollLockManager || new ScrollLockManager(),
appBridge,
};
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import * as React from 'react';
import * as appBridge from '@shopify/app-bridge';
import {noop} from '@shopify/javascript-utilities/other';
import * as targets from '@shopify/react-utilities/target';
import createAppProviderContext, {
setClientInterfaceHook,
} from '../createAppProviderContext';
import {StickyManager} from '../../withSticky';
import StickyManager from '../../StickyManager';
import ScrollLockManager from '../../ScrollLockManager';

jest.mock('../../Intl', () => ({
Expand Down Expand Up @@ -41,15 +40,11 @@ describe('createAppProviderContext()', () => {
const context = createAppProviderContext();

expect(context).toMatchObject({
polaris: {
intl: expect.any(Intl),
link: expect.any(Link),
stickyManager: expect.any(StickyManager),
scrollLockManager: expect.any(ScrollLockManager),
subscribe: noop,
unsubscribe: noop,
appBridge: undefined,
},
intl: expect.any(Intl),
link: expect.any(Link),
stickyManager: expect.any(StickyManager),
scrollLockManager: expect.any(ScrollLockManager),
appBridge: undefined,
});
});

Expand Down Expand Up @@ -86,24 +81,20 @@ describe('createAppProviderContext()', () => {
});

expect(context).toMatchObject({
polaris: {
intl: expect.any(Intl),
link: expect.any(Link),
stickyManager: expect.any(StickyManager),
scrollLockManager: expect.any(ScrollLockManager),
subscribe: noop,
unsubscribe: noop,
appBridge: {
apiKey,
forceRedirect: undefined,
shopOrigin: undefined,
dispatch: expect.any(Function),
localOrigin: '',
featuresAvailable: expect.any(Function),
getState: expect.any(Function),
subscribe: expect.any(Function),
error: expect.any(Function),
},
intl: expect.any(Intl),
link: expect.any(Link),
stickyManager: expect.any(StickyManager),
scrollLockManager: expect.any(ScrollLockManager),
appBridge: {
apiKey,
forceRedirect: undefined,
shopOrigin: undefined,
dispatch: expect.any(Function),
localOrigin: '',
featuresAvailable: expect.any(Function),
getState: expect.any(Function),
subscribe: expect.any(Function),
error: expect.any(Function),
},
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import {PolarisContext} from '../../../types';
import {
createThemeContext,
ThemeContext as CreateThemeContext,
ThemeProviderContextType as CreateThemeContext,
} from '../../../ThemeProvider';
import {AppProviderProps} from '../../types';
import {StickyManager} from '../withSticky';
import StickyManager from '../StickyManager';
import createAppProviderContext, {
CreateAppProviderContext,
} from '../createAppProviderContext';
Expand Down Expand Up @@ -42,5 +42,5 @@ export default function createPolarisContext(
? createThemeContext(themeContext)
: createThemeContext();

return {...appProvider, ...theme};
return {...appProvider, theme};
}
Loading