Skip to content

Commit

Permalink
chore(Snackbar): Refactor to MDC Foundation #197 #141 #239
Browse files Browse the repository at this point in the history
  • Loading branch information
James Friedman committed Jun 4, 2018
1 parent 089ef66 commit 6c29300
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 79 deletions.
176 changes: 97 additions & 79 deletions src/Snackbar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ import type { SimpleTagPropsT } from '../Base';

import * as React from 'react';
import { MDCSnackbar } from '@material/snackbar/dist/mdc.snackbar';
import { getCorrectEventName } from '@material/animation/dist/mdc.animation';
import { noop } from '../Base/utils/noop';
import Button from '../Button';
import { simpleTag, withMDC } from '../Base';
import { syncFoundationProp, withFoundation } from '../Base/MDCFoundation';

export const SnackbarRoot = simpleTag({
displayName: 'SnackbarRoot',
Expand Down Expand Up @@ -68,37 +70,29 @@ export type SnackbarPropsT = {
alignStart?: boolean
} & SimpleTagPropsT;

const showSnackbar = (props, api) => {
const {
message,
timeout,
actionHandler,
actionText,
multiline,
actionOnBottom
} = props;

api.show({
message,
timeout,
actionHandler,
actionText: actionText || ' ',
multiline,
actionOnBottom
});
};

/**
* A Snackbar component for notifications.
*/
export const Snackbar = withMDC({
mdcConstructor: MDCSnackbar,
mdcElementRef: true,
mdcEvents: {
'MDCSnackbar:show': (evt, props, api) => props.onShow(evt),
'MDCSnackbar:hide': (evt, props, api) => props.onHide(evt)
},
defaultProps: {
export class Snackbar extends withFoundation({
constructor: MDCSnackbar,
adapter: {
removeClass: function(className) {
// only remove if we still have a reference to our root.
this.root_ && this.root_.classList.remove(className);
},
registerTransitionEndHandler: function(handler) {
// only add if we still have a reference to our root.
this.root_ &&
this.root_.addEventListener(
getCorrectEventName(window, 'transitionend'),
handler
);
}
}
})<SnackbarPropsT> {
static displayName = 'Snackbar';

static defaultProps = {
show: false,
onHide: noop,
onShow: noop,
Expand All @@ -109,68 +103,92 @@ export const Snackbar = withMDC({
multiline: false,
actionOnBottom: false,
dismissesOnAction: true
},
onUpdate: (props, nextProps, api) => {
if (api) {
const { show, dismissesOnAction } = nextProps;
api.dismissesOnAction = dismissesOnAction;
};

if ((!props || show !== props.show) && show) {
showSnackbar(nextProps, api);
}
}
isShowing_ = false;

get isShowing() {
return this.isShowing_;
}
})(
class extends React.Component<SnackbarPropsT> {
static displayName = 'Snackbar';

render() {
set isShowing(isShowing: boolean) {
this.isShowing_ = isShowing;
}

syncWithProps(nextProps: MDCSnackbar) {
syncFoundationProp(
nextProps.dismissesOnAction,
this.dismissesOnAction,
() => (this.dismissesOnAction = nextProps.dismissesOnAction)
);

syncFoundationProp(nextProps.show, this.isShowing, () => {
const {
show,
message,
timeout,
actionHandler,
actionText,
multiline,
actionOnBottom,
dismissesOnAction,
onHide,
onShow,
children,
// $FlowFixMe
mdcElementRef,
...rest
} = this.props;

const isJSX = typeof message === 'object';
const snackbarTextStyle = {};
if (isJSX) {
snackbarTextStyle.display = 'none';
}
actionOnBottom
} = nextProps;

this.show({
message,
timeout,
actionHandler,
actionText: actionText || ' ',
multiline,
actionOnBottom
});
});
}

render() {
const {
show,
message,
timeout,
actionHandler,
actionText,
multiline,
actionOnBottom,
dismissesOnAction,
onHide,
onShow,
children,
...rest
} = this.props;

const { root_ } = this.foundationRefs;

const snackbarActionWrapperStyle = !actionText
? {
display: 'none'
}
: {};

/**
* The double SnackbarText below is a hack to allow for rendering JSX
* The real message gets rendered in the hidden container, and the second one is
* ignored and shows th rendered content :)
*/
return (
<SnackbarRoot elementRef={mdcElementRef} {...rest}>
<SnackbarText style={snackbarTextStyle} />
{isJSX && <SnackbarText>{message}</SnackbarText>}
<SnackbarActionWrapper style={snackbarActionWrapperStyle}>
<SnackbarActionButton />
</SnackbarActionWrapper>
{children}
</SnackbarRoot>
);
const isJSX = typeof message === 'object';
const snackbarTextStyle = {};
if (isJSX) {
snackbarTextStyle.display = 'none';
}

const snackbarActionWrapperStyle = !actionText
? {
display: 'none'
}
: {};

/**
* The double SnackbarText below is a hack to allow for rendering JSX
* The real message gets rendered in the hidden container, and the second one is
* ignored and shows th rendered content :)
*/
return (
<SnackbarRoot {...rest} elementRef={root_}>
<SnackbarText style={snackbarTextStyle} />
{isJSX && <SnackbarText>{message}</SnackbarText>}
<SnackbarActionWrapper style={snackbarActionWrapperStyle}>
<SnackbarActionButton />
</SnackbarActionWrapper>
{children}
</SnackbarRoot>
);
}
);
}

export default Snackbar;
20 changes: 20 additions & 0 deletions src/Snackbar/snackbar-ssr.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* @jest-environment node
*/
import React from 'react';
import { renderToString as mount } from 'react-dom/server';
import { Snackbar } from './';

describe('Snackbar', () => {
it('renders', () => {
mount(
<Snackbar
show
onHide={evt => {}}
message="This is a new message"
actionText="Action"
actionHandler={() => alert('Action clicked')}
/>
);
});
});

0 comments on commit 6c29300

Please sign in to comment.