Skip to content
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
2 changes: 0 additions & 2 deletions UNRELEASED.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@ Use [the changelog guidelines](https://git.io/polaris-changelog-guidelines) to f

### Code quality

- Upgraded the `Banner`, `Card`, and `Modal` components from legacy context API to use createContext ([#786](https://github.com/Shopify/polaris-react/pull/786))

### Deprecations

- Deprecated `Navigation.UserMenu` in favor of `TopBar.UserMenu` ([#849](https://github.com/Shopify/polaris-react/pull/849))
18 changes: 5 additions & 13 deletions src/components/Banner/Banner.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,17 @@
import * as React from 'react';
import {classNames, variationName} from '@shopify/react-utilities/styles';

import compose from '@shopify/react-compose';
import {
Action,
DisableableAction,
LoadableAction,
WithContextTypes,
contentContextTypes,
} from '../../types';
import Button, {buttonFrom} from '../Button';
import Heading from '../Heading';
import ButtonGroup from '../ButtonGroup';
import UnstyledLink from '../UnstyledLink';
import Icon, {Props as IconProps} from '../Icon';
import {Consumer, WithinContentContext} from '../WithinContentContext';
import withContext from '../WithContext';
import {withAppProvider, WithAppProviderProps} from '../AppProvider';

import * as styles from './Banner.scss';

Expand Down Expand Up @@ -44,9 +40,9 @@ export interface Props {
onDismiss?(): void;
}

export type CombinedProps = Props & WithContextTypes<WithinContentContext>;
export default class Banner extends React.PureComponent<Props, never> {
static contextTypes = contentContextTypes;

export class Banner extends React.PureComponent<CombinedProps, never> {
render() {
const {
icon,
Expand All @@ -56,8 +52,8 @@ export class Banner extends React.PureComponent<CombinedProps, never> {
children,
status,
onDismiss,
context: {withinContentContainer},
} = this.props;
const {withinContentContainer} = this.context;

let color: IconProps['color'];
let defaultIcon: IconProps['source'];
Expand Down Expand Up @@ -86,6 +82,7 @@ export class Banner extends React.PureComponent<CombinedProps, never> {
color = 'inkLighter';
defaultIcon = fallbackIcon;
}

const className = classNames(
styles.Banner,
status && styles[variationName('status', status)],
Expand Down Expand Up @@ -201,8 +198,3 @@ function secondaryActionFrom(action: Action) {
</button>
);
}

export default compose<Props>(
withContext<Props, WithAppProviderProps, WithinContentContext>(Consumer),
withAppProvider(),
)(Banner);
18 changes: 8 additions & 10 deletions src/components/Banner/tests/Banner.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import fallbackIcon from '../icons/flag.svg';
import warningIcon from '../icons/circle-alert.svg';
import criticalIcon from '../icons/circle-barred.svg';
import infoIcon from '../icons/circle-information.svg';
import {Provider} from '../../WithinContentContext';

describe('<Banner />', () => {
it('renders a title', () => {
Expand Down Expand Up @@ -96,15 +95,14 @@ describe('<Banner />', () => {
};

const bannerWithContentContext = mountWithAppProvider(
<Provider value={mockContext}>
<Banner
action={{
content: 'Primary action',
}}
>
Some content
</Banner>
</Provider>,
<Banner
action={{
content: 'Primary action',
}}
>
Some content
</Banner>,
{context: mockContext},
);

it('renders a slim button with contentContext', () => {
Expand Down
30 changes: 13 additions & 17 deletions src/components/Card/Card.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import * as React from 'react';
import {classNames} from '@shopify/react-utilities/styles';
import {autobind} from '@shopify/javascript-utilities/decorators';

import {Action, DisableableAction} from '../../types';
import {Action, DisableableAction, contentContextTypes} from '../../types';
import {buttonFrom} from '../Button';
import ButtonGroup from '../ButtonGroup';
import {Provider, WithinContentContext} from '../WithinContentContext';

import {Header, Section} from './components';
import * as styles from './Card.scss';
Expand All @@ -30,6 +28,13 @@ export interface Props {
export default class Card extends React.PureComponent<Props, never> {
static Section = Section;
static Header = Header;
static childContextTypes = contentContextTypes;

getChildContext() {
return {
withinContentContainer: true,
};
}

render() {
const {
Expand Down Expand Up @@ -69,20 +74,11 @@ export default class Card extends React.PureComponent<Props, never> {
) : null;

return (
<Provider value={this.getContext}>
<div className={className}>
{headerMarkup}
{content}
{footerMarkup}
</div>
</Provider>
<div className={className}>
{headerMarkup}
{content}
{footerMarkup}
</div>
);
}

@autobind
get getContext(): WithinContentContext {
return {
withinContentContainer: true,
};
}
}
27 changes: 12 additions & 15 deletions src/components/Card/tests/Card.test.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,25 @@
import * as React from 'react';
import {mountWithAppProvider} from 'test-utilities';
import {Card, Badge} from 'components';

import {Consumer, WithinContentContext} from '../../WithinContentContext';
import {contentContextTypes} from '../../../types';

describe('<Card />', () => {
it('has a child with prop withinContentContainer set to true', () => {
function TestComponent(_: WithinContentContext) {
return null;
}
it('has a child with contentContext', () => {
const Child: React.SFC<{}> = (_props, context) =>
context.withinContentContainer ? <div /> : null;
Child.contextTypes = contentContextTypes;

const component = mountWithAppProvider(
const containedChild = mountWithAppProvider(
<Card>
<Consumer>
{(props) => {
return <TestComponent {...props} />;
}}
</Consumer>
<Child />
</Card>,
);

expect(component.find(TestComponent).prop('withinContentContainer')).toBe(
true,
);
const div = containedChild
.find(Child)
.find('div')
.first();
expect(div.exists()).toBe(true);
});

it('has a header tag when the title is a string', () => {
Expand Down
31 changes: 15 additions & 16 deletions src/components/Modal/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import {focusFirstFocusableNode} from '@shopify/javascript-utilities/focus';
import {createUniqueIDFactory} from '@shopify/javascript-utilities/other';
import {wrapWithComponent} from '@shopify/react-utilities';
import {Modal as AppBridgeModal} from '@shopify/app-bridge/actions';
import {Provider, WithinContentContext} from '../WithinContentContext';

import {contentContextTypes} from '../../types';
import {transformActions} from '../../utilities/app-bridge-transformers';

import {withAppProvider, WithAppProviderProps} from '../AppProvider';
Expand Down Expand Up @@ -91,6 +91,8 @@ const APP_BRIDGE_PROPS: (keyof Props)[] = [
];

export class Modal extends React.Component<CombinedProps, State> {
static childContextTypes = contentContextTypes;

static Dialog = Dialog;
static Section = Section;
focusReturnPointNode: HTMLElement;
Expand All @@ -105,6 +107,12 @@ export class Modal extends React.Component<CombinedProps, State> {
| AppBridgeModal.ModalIframe
| undefined;

getChildContext() {
return {
withinContentContainer: true,
};
}

componentDidMount() {
if (this.props.polaris.appBridge == null) {
return;
Expand Down Expand Up @@ -282,14 +290,12 @@ export class Modal extends React.Component<CombinedProps, State> {
const animated = !instant;

return (
<Provider value={this.getContext}>
<Portal idPrefix="modal">
<TransitionGroup appear={animated} enter={animated} exit={animated}>
{dialog}
</TransitionGroup>
{backdrop}
</Portal>
</Provider>
<Portal idPrefix="modal">
<TransitionGroup appear={animated} enter={animated} exit={animated}>
{dialog}
</TransitionGroup>
{backdrop}
</Portal>
);
}

Expand Down Expand Up @@ -364,13 +370,6 @@ export class Modal extends React.Component<CombinedProps, State> {
},
};
}

@autobind
get getContext(): WithinContentContext {
return {
withinContentContainer: true,
};
}
}

function isIframeModal(
Expand Down
29 changes: 13 additions & 16 deletions src/components/Modal/tests/Modal.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ import {noop} from '@shopify/javascript-utilities/other';
import {animationFrame} from '@shopify/jest-dom-mocks';
import {findByTestID, trigger, mountWithAppProvider} from 'test-utilities';
import {Badge, Spinner, Portal, Scrollable} from 'components';
import {contentContextTypes} from '../../../types';
import {Footer, Dialog} from '../components';
import Modal from '../Modal';

import {Consumer, WithinContentContext} from '../../WithinContentContext';

jest.mock('../../../utilities/app-bridge-transformers', () => ({
...require.requireActual('../../../utilities/app-bridge-transformers'),
transformActions: jest.fn((...args) => args),
Expand All @@ -24,23 +23,21 @@ describe('<Modal>', () => {
});

it('has a child with contentContext', () => {
function TestComponent(_: WithinContentContext) {
return null;
}

const component = mountWithAppProvider(
<Modal onClose={jest.fn()} open>
<Consumer>
{(props) => {
return <TestComponent {...props} />;
}}
</Consumer>
const Child: React.SFC<{}> = (_props, context) =>
context.withinContentContainer ? <div /> : null;
Child.contextTypes = contentContextTypes;

const containedChild = mountWithAppProvider(
<Modal open onClose={jest.fn()}>
<Child />
</Modal>,
);

expect(component.find(TestComponent).prop('withinContentContainer')).toBe(
true,
);
const div = containedChild
.find(Child)
.find('div')
.first();
expect(div.exists()).toBe(true);
});

describe('src', () => {
Expand Down
11 changes: 0 additions & 11 deletions src/components/WithinContentContext/WithinContentContext.tsx

This file was deleted.

1 change: 0 additions & 1 deletion src/components/WithinContentContext/index.ts

This file was deleted.

6 changes: 6 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import * as PropTypes from 'prop-types';
import {ValidationMap} from 'react';
// eslint-disable-next-line shopify/strict-component-boundaries
import {Props as IconProps} from './components/Icon';

Expand Down Expand Up @@ -233,6 +235,10 @@ export enum Key {
SingleQuote = 222,
}

export const contentContextTypes: ValidationMap<any> = {
withinContentContainer: PropTypes.bool,
};

export interface WithContextTypes<IJ> {
context: IJ;
}