Skip to content

Commit

Permalink
feat(Snackbar): fixes #42 hide extra elements
Browse files Browse the repository at this point in the history
Styles the snackbar button to be hidden when no action is set and allows for extra children.
  • Loading branch information
James Friedman committed Dec 6, 2017
1 parent d03bcfa commit e460304
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 96 deletions.
136 changes: 72 additions & 64 deletions src/Snackbar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import { simpleTag, withMDC } from '../Base';
import type { SimpleTagPropsT } from '../Base';

type SnackbarRootT = {
/* Aligns the Snackbar to the start of the screen. */
alignStart?: boolean
/* Aligns the Snackbar to the start of the screen. */
alignStart?: boolean
} & SimpleTagPropsT;

export const SnackbarRoot: React.ComponentType<SnackbarRootT> = simpleTag({
Expand Down Expand Up @@ -49,24 +49,24 @@ export const SnackbarActionButton = simpleTag({
});

type SnackbarPropsT = {
/** Show the Snackbar. */
show?: boolean,
/** A callback thats fired when the Snackbar closes. */
onClose?: () => mixed,
/** A string or other renderable JSX to be used as the message body. */
message?: React.Node,
/** Milliseconds to show the Snackbar for. */
timeout?: number,
/** Callback that fires when action is pressed. The actionText property must be set to use this. */
actionHandler?: () => mixed,
/** Label for the action button. */
actionText?: React.Node,
/** Lets the Snackbar text overflow onto multiple lines. */
multiline?: boolean,
/** Places the action underneath the message text. */
actionOnBottom?: boolean,
/** Whether or not the Snackbar dismisses on the action press. */
dismissesOnAction?: boolean
/** Show the Snackbar. */
show?: boolean,
/** A callback thats fired when the Snackbar closes. */
onClose?: () => mixed,
/** A string or other renderable JSX to be used as the message body. */
message?: React.Node,
/** Milliseconds to show the Snackbar for. */
timeout?: number,
/** Callback that fires when action is pressed. The actionText property must be set to use this. */
actionHandler?: () => mixed,
/** Label for the action button. */
actionText?: React.Node,
/** Lets the Snackbar text overflow onto multiple lines. */
multiline?: boolean,
/** Places the action underneath the message text. */
actionOnBottom?: boolean,
/** Whether or not the Snackbar dismisses on the action press. */
dismissesOnAction?: boolean
};

const showSnackbar = (props, api) => {
Expand All @@ -81,19 +81,19 @@ const showSnackbar = (props, api) => {
} = props;
const timer = setTimeout(() => onClose(), timeout || 2750);
const wrappedActionHandler =
actionHandler && api.dismissesOnAction ?
() => {
actionHandler();
clearTimeout(timer);
onClose();
} :
actionHandler;
actionHandler && api.dismissesOnAction ?
() => {
actionHandler();
clearTimeout(timer);
onClose();
} :
actionHandler;

api.show({
message,
timeout,
actionHandler: wrappedActionHandler,
actionText,
actionText: actionText || ' ',
multiline,
actionOnBottom
});
Expand Down Expand Up @@ -128,44 +128,52 @@ export const Snackbar = withMDC({
}
})(
class extends React.Component<SnackbarPropsT> {
static displayName = 'Snackbar';

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

const isJSX = typeof message === 'object';
const snackbarTextStyle = {};
if (isJSX) {
snackbarTextStyle.display = 'none';
}
static displayName = 'Snackbar';

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

/**
* 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>
<SnackbarActionButton />
</SnackbarActionWrapper>
</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 elementRef={mdcElementRef} {...rest}>
<SnackbarText style={snackbarTextStyle} />
{isJSX && <SnackbarText>{message}</SnackbarText>}
<SnackbarActionWrapper style={snackbarActionWrapperStyle}>
<SnackbarActionButton />
</SnackbarActionWrapper>
{children}
</SnackbarRoot>
);
}
}
);

Expand Down
64 changes: 32 additions & 32 deletions src/Snackbar/snackbar.story.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,38 +7,38 @@ import { Snackbar } from './';
import { storyWithState } from '../Base/story-with-state';

const SnackbarStory = storyWithState(
state => ({
show: boolean('show', state.show !== undefined ? state.show : true),
alignStart: boolean('alignStart', state.alignStart || false),
message: text('message', state.message || 'This is a new message'),
actionText: text('actionText', state.actionText || 'Action'),
timeout: number('timeout', state.timeout || 2750),
multiline: boolean('multiline', state.multiline || false),
actionOnBottom: boolean('actionOnBottom', state.actionOnBottom || false),
dismissesOnAction: boolean(
'dismissesOnAction',
state.dismissesOnAction !== undefined ? state.dismissesOnAction : true
)
}),
function() {
return (
<Snackbar
show={this.state.show}
message={this.state.message}
alignStart={this.state.alignStart}
actionOnBottom={this.state.actionOnBottom}
dismissesOnAction={this.state.dismissesOnAction}
multiline={this.state.multiline}
actionText={this.state.actionText}
timeout={this.state.timeout}
onClose={() => {
this.setState({ show: false });
action('onClose')();
}}
actionHandler={action('actionHandler')}
/>
);
}
state => ({
show: boolean('show', state.show !== undefined ? state.show : true),
alignStart: boolean('alignStart', state.alignStart || false),
message: text('message', state.message || 'This is a new message'),
actionText: text('actionText', state.actionText || 'Action'),
timeout: number('timeout', state.timeout || 2750),
multiline: boolean('multiline', state.multiline || false),
actionOnBottom: boolean('actionOnBottom', state.actionOnBottom || false),
dismissesOnAction: boolean(
'dismissesOnAction',
state.dismissesOnAction !== undefined ? state.dismissesOnAction : true
)
}),
function() {
return (
<Snackbar
show={this.state.show}
message={this.state.message}
alignStart={this.state.alignStart}
actionOnBottom={this.state.actionOnBottom}
dismissesOnAction={this.state.dismissesOnAction}
multiline={this.state.multiline}
actionText={this.state.actionText}
timeout={this.state.timeout}
onClose={() => {
this.setState({ show: false });
action('onClose')();
}}
actionHandler={action('actionHandler')}
/>
);
}
);

storiesOf('Snackbar', module).add('Snackbar', () => <SnackbarStory />);

0 comments on commit e460304

Please sign in to comment.