Skip to content

Instantly share code, notes, and snippets.

@thomasmaclean
Last active December 27, 2017 16:29
Show Gist options
  • Save thomasmaclean/9032227db3911fc563cea2c564e3b609 to your computer and use it in GitHub Desktop.
Save thomasmaclean/9032227db3911fc563cea2c564e3b609 to your computer and use it in GitHub Desktop.
Redux Alert system
import React, { Component } from 'react';
import CookieWarning from './CookieWarning';
import PushDown from './PushDown';
export default class Alerts extends Component {
render() {
require('src/sass/components/push-down-notifications.scss');
return (
<PushDown>
<CookieWarning><button /></CookieWarning>
</PushDown>
);
}
}
import React, { Component, PropTypes } from 'react';
const enhanceWithClickOutside = require('react-click-outside');
class AlertWrapper extends Component {
static propTypes = {
children: PropTypes.node,
className: PropTypes.string,
onClickOutside: PropTypes.func
};
handleClickOutside() {
this.props.onClickOutside();
}
render() {
return (<div className={this.props.className}>{this.props.children}</div>);
}
}
export default enhanceWithClickOutside(AlertWrapper);
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { registerAlert, openAlert, closeAlert } from './redux-alert';
import { supportsLocalStorage, supportsCookies } from './cookie-helpers.js';
const id = 'CookieWarning';
class CookieWarning extends Component {
static propTypes = {
title: PropTypes.string,
description: PropTypes.string,
children: PropTypes.object,
cookiesEnabled: PropTypes.bool,
active: PropTypes.string,
registerAlert: PropTypes.func,
openAlert: PropTypes.func,
closeAlert: PropTypes.func
};
static defaultProps = {
title: 'Cookies Required',
description: `Our site uses cookies to remember you when you come back again. Please enable
cookies in your browser options for the best experience.`
};
componentDidMount() {
this.props.registerAlert(this.constructor.name);
// Cookies comes from request.headers
const { active } = this.props;
const cookiesEnabled = supportsLocalStorage() && supportsCookies();
const openNow = active === this.constructor.name;
const closedBefore = supportsLocalStorage() && window.localStorage.getItem(id) === 'closed';
if (!cookiesEnabled && (!openNow || closedBefore)) {
this.props.openAlert(id);
}
}
componentWillReceiveProps(nextProps) {
if (this.props.active === id && nextProps.active !== id && supportsLocalStorage()) {
window.localStorage.setItem(id, 'closed');
}
}
render() {
return this.props.active === id ? (
<div className="alert cookie-warning">
<div className="alert__icon alert-cookie"></div>
<div className="alert__content">
<div className="alert__title">{this.props.title}</div>
<div className="alert__description">{this.props.description}</div>
<div className="alert__button-container" onClick={ this.props.closeAlert }>
{this.props.children || <button>Okay</button>}
</div>
</div>
</div>
) : null;
}
}
export default connect(state => ({
active: state.alert.active
}), { registerAlert, openAlert, closeAlert })(CookieWarning);
import React, { Component, PropTypes } from 'react';
import AlertWrapper from './AlertWrapper';
import classnames from 'classnames';
import { connect } from 'react-redux';
import { closeAlert } from './redux-alert';
class PushDown extends Component {
static propTypes = {
children: PropTypes.node,
active: PropTypes.string,
registry: PropTypes.object,
closeAlert: PropTypes.func
};
componentDidMount() {
this.body = document.querySelector('body');
this.nav = document.querySelector('.nav');
this.showHide();
}
componentDidUpdate() {
this.showHide();
}
alertClass = 'push-down__content-wrapper';
showHide() {
const { active, registry } = this.props;
const alertWrapper = document.querySelector(`.${this.alertClass}`);
if (!!active) {
const alertInfo = registry[active];
const alert = document.querySelector(`.${this.alertClass} .alert`);
const contentHeight = `${alertInfo.height || alert.offsetHeight}px`;
window.setTimeout(() => { alertWrapper.style.overflow = 'visible'; }, 250);
alertWrapper.style.height = contentHeight;
this.refs.container.style.height = contentHeight;
this.refs.overlay.style.transform = `translateY(${contentHeight}px)`;
this.body.style.overflow = 'hidden';
this.body.style.width = 'auto';
this.nav.style.top = contentHeight;
} else {
alertWrapper.style.height = 0;
alertWrapper.style.removeProperty('overflow');
this.refs.container.style.height = 0;
this.refs.overlay.style.transform = `translateY(0px)`;
this.body.style.removeProperty('width');
this.body.style.removeProperty('overflow');
this.nav.style.removeProperty('top');
}
}
render() {
const { active, children } = this.props;
const pushDownWrapperClass = classnames('push-down__container', { alert__active: !!active });
return (
<div className={pushDownWrapperClass} ref="container">
<div className="push-down__overlay" ref="overlay"></div>
<div className="push-down__close alert-close" onClick={this.props.closeAlert}></div>
<AlertWrapper onClickOutside={this.props.closeAlert} className={this.alertClass}>
{children}
</AlertWrapper>
</div>
);
}
}
export default connect(state => ({
active: state.alert.active,
registry: state.alert.registry
}), { closeAlert })(PushDown)
const ALERT_REGISTER = 'alert/register';
const ALERT_OPEN = 'alert/open';
const ALERT_CLOSE = 'alert/close';
const initialState = {
registry: {},
active: '',
queue: []
};
export default function reducer(state = initialState, action = {}) {
switch (action.type) {
case ALERT_REGISTER:
return {
...state,
registry: {
...state.registry,
...action.alertInfo
}
};
case ALERT_OPEN:
return (state.active && state.alert !== action.alert && action.alert) ? {
...state,
queue: [...state.queue, action.alert]
} : {
...state,
active: action.alert
};
case ALERT_CLOSE:
return (state.queue) ? {
...state,
queue: state.queue.slice(1),
active: state.queue[0] || ''
} : {
active: ''
};
default:
return state;
}
}
export function registerAlert(name, info = {}) {
const alertInfo = {};
alertInfo[name] = info;
return {
type: ALERT_REGISTER,
alertInfo
};
}
export function openAlert(name) {
return {
type: ALERT_OPEN,
alert: name
};
}
export function closeAlert() {
return {
type: ALERT_CLOSE
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment