diff --git a/src/components/modal/mute/modal.jsx b/src/components/modal/mute/modal.jsx
new file mode 100644
index 000000000..887abcebb
--- /dev/null
+++ b/src/components/modal/mute/modal.jsx
@@ -0,0 +1,115 @@
+const bindAll = require('lodash.bindall');
+const PropTypes = require('prop-types');
+const React = require('react');
+const Modal = require('../base/modal.jsx');
+const ModalInnerContent = require('../base/modal-inner-content.jsx');
+const Button = require('../../forms/button.jsx');
+const Progression = require('../../progression/progression.jsx');
+const FlexRow = require('../../flex-row/flex-row.jsx');
+const MuteStep = require('./mute-step.jsx');
+const classNames = require('classnames');
+require('./modal.scss');
+
+class MuteModal extends React.Component {
+ constructor (props) {
+ super(props);
+ bindAll(this, [
+ 'handleNext',
+ 'handlePrevious'
+ ]);
+ this.state = {
+ step: 0
+ };
+ }
+ handleNext () {
+ this.setState({
+ step: this.state.step + 1
+ });
+ }
+ handlePrevious () {
+ // This shouldn't get called when we're on the first step, but
+ // the Math.max is here as a safeguard so state doesn't go negative.
+ this.setState({
+ step: Math.max(0, this.state.step - 1)
+ });
+ }
+ render () {
+ return (
+
+
+
+
+
+
+ If you think something could be better, you can say something you like about the project,
+ and make a suggestion about how to improve it. For example, you could say:
+
+
+
+
+ Once X minutes have passed, you will be able to comment again.
+
+
+ If you would like more information, you can read the Scratch community guidelines.
+
+
+
+
+
+ {this.state.step > 0 ? (
+
+
+ Back
+
+
+ ) : null }
+ {this.state.step >= 1 ? (
+
+
+ Close
+
+
+ ) : (
+
+
+ Next
+
+
+ )}
+
+
+
+ );
+ }
+}
+
+MuteModal.propTypes = {
+ onRequestClose: PropTypes.func
+};
+
+module.exports = MuteModal;
diff --git a/src/components/modal/mute/modal.scss b/src/components/modal/mute/modal.scss
new file mode 100644
index 000000000..0ceabd82c
--- /dev/null
+++ b/src/components/modal/mute/modal.scss
@@ -0,0 +1,60 @@
+@import "../../../colors";
+@import "../../../frameless";
+
+.modal-mute {
+ width: 30rem;
+
+ .mute-modal-header {
+ box-shadow: inset 0 -1px 0 0 $ui-mint-green;
+ background-color: $ui-mint-green;
+ border-radius: 1rem 1rem 0 0;
+ }
+ .mute-step {
+ display: flex;
+ padding: 48px 16px;
+ }
+ .mute-content {
+ padding-top: 16px;
+ }
+ .mute-inner-content {
+ padding: 0 32px;
+ }
+ .left-column {
+ padding-right: 32px;
+ }
+ .mute-header {
+ font-size: 1.5rem;
+ font-weight: bold;
+ line-height: 2rem;
+ }
+ .mute-bottom-row {
+ padding-top: 32px;
+ }
+ .bottom-img {
+ width: 100%;
+ }
+ .mute-side-image {
+ margin-left: -49px;
+ }
+ .side-img {
+ height: 212px;
+ width: 129px;
+ }
+ .nav-divider {
+ border-top: 1px solid $ui-blue-25percent;
+ }
+ .mute-nav {
+ display:flex;
+ justify-content: space-between;
+ padding: 24px 0;
+ }
+ .back-button {
+ margin-top: 0;
+ margin-bottom: 0;
+ }
+ .next-button, .close-button {
+ margin-left: auto;
+ margin-top: 0;
+ margin-bottom: 0;
+ }
+}
diff --git a/src/components/modal/mute/mute-step.jsx b/src/components/modal/mute/mute-step.jsx
new file mode 100644
index 000000000..fe7c75fed
--- /dev/null
+++ b/src/components/modal/mute/mute-step.jsx
@@ -0,0 +1,55 @@
+const PropTypes = require('prop-types');
+const React = require('react');
+const classNames = require('classnames');
+
+const FlexRow = require('../../flex-row/flex-row.jsx');
+require('./modal.scss');
+
+const MuteStep = ({
+ bottomImg,
+ bottomImgClass,
+ children,
+ header,
+ sideImg,
+ sideImgClass
+}) => (
+
+ {sideImg &&
+
+
+
+
+
+ }
+
+
+ {header}
+
+
+ {children}
+
+
+ {bottomImg &&
+
+ }
+
+
+
+);
+
+MuteStep.propTypes = {
+ bottomImg: PropTypes.string,
+ bottomImgClass: PropTypes.string,
+ children: PropTypes.node,
+ header: PropTypes.string,
+ sideImg: PropTypes.string,
+ sideImgClass: PropTypes.string
+};
+
+module.exports = MuteStep;
diff --git a/test/unit/components/mute-modal.test.jsx b/test/unit/components/mute-modal.test.jsx
new file mode 100644
index 000000000..f7a1e7e63
--- /dev/null
+++ b/test/unit/components/mute-modal.test.jsx
@@ -0,0 +1,93 @@
+import React from 'react';
+import {shallowWithIntl} from '../../helpers/intl-helpers.jsx';
+import {mountWithIntl} from '../../helpers/intl-helpers.jsx';
+import MuteModal from '../../../src/components/modal/mute/modal';
+import Modal from '../../../src/components/modal/base/modal';
+
+
+describe('MuteModalTest', () => {
+
+ test('Mute Modal rendering', () => {
+ const component = shallowWithIntl(
+
+ );
+ expect(component.find('div.mute-modal-header').exists()).toEqual(true);
+
+ });
+
+ test('Mute Modal only shows next button on initial step', () => {
+ const component = mountWithIntl(
+
+ );
+ expect(component.find('div.mute-nav').exists()).toEqual(true);
+ expect(component.find('button.next-button').exists()).toEqual(true);
+ expect(component.find('button.next-button').getElements()[0].props.onClick)
+ .toEqual(component.instance().handleNext);
+ expect(component.find('button.close-button').exists()).toEqual(false);
+ expect(component.find('button.back-button').exists()).toEqual(false);
+ });
+
+ test('Mute Modal shows back & close button on last step', () => {
+ const component = mountWithIntl(
+
+ );
+ // Step 1 is the last step.
+ component.instance().setState({step: 1});
+ component.update();
+
+ expect(component.find('div.mute-nav').exists()).toEqual(true);
+ expect(component.find('button.next-button').exists()).toEqual(false);
+ expect(component.find('button.back-button').exists()).toEqual(true);
+ expect(component.find('button.back-button').getElements()[0].props.onClick)
+ .toEqual(component.instance().handlePrevious);
+ expect(component.find('button.close-button').exists()).toEqual(true);
+ expect(component.find('button.close-button').getElements()[0].props.onClick)
+ .toEqual(component.instance().props.onRequestClose);
+ });
+
+ test('Mute modal sends correct props to Modal', () => {
+ const closeFn = jest.fn();
+ const component = shallowWithIntl(
+
+ );
+ const modal = component.find(Modal);
+ expect(modal).toHaveLength(1);
+ expect(modal.props().showCloseButton).toBe(false);
+ expect(modal.props().isOpen).toBe(true);
+ expect(modal.props().className).toBe('modal-mute');
+ expect(modal.props().onRequestClose).toBe(closeFn);
+ });
+
+ test('Mute modal handle next step', () => {
+ const closeFn = jest.fn();
+ const component = shallowWithIntl(
+
+ );
+ expect(component.instance().state.step).toBe(0);
+ component.instance().handleNext();
+ expect(component.instance().state.step).toBe(1);
+ });
+
+ test('Mute modal handle previous step', () => {
+ const component = shallowWithIntl(
+
+ );
+ component.instance().setState({step: 1});
+
+ component.instance().handlePrevious();
+ expect(component.instance().state.step).toBe(0);
+ });
+
+ test('Mute modal handle previous step stops at 0', () => {
+ const component = shallowWithIntl(
+
+ );
+ component.instance().setState({step: 0});
+ component.instance().handlePrevious();
+ expect(component.instance().state.step).toBe(0);
+ });
+});
diff --git a/test/unit/components/mute-step.test.jsx b/test/unit/components/mute-step.test.jsx
new file mode 100644
index 000000000..1d77e5d7e
--- /dev/null
+++ b/test/unit/components/mute-step.test.jsx
@@ -0,0 +1,49 @@
+import React from 'react';
+import {mountWithIntl} from '../../helpers/intl-helpers.jsx';
+import MuteStep from '../../../src/components/modal/mute/mute-step';
+
+describe('MuteStepTest', () => {
+ test('Mute Step with no images ', () => {
+ const component = mountWithIntl(
+
+ );
+ expect(component.find('div.mute-step').exists()).toEqual(true);
+ expect(component.find('div.mute-header').exists()).toEqual(true);
+ expect(component.find('div.mute-right-column').exists()).toEqual(true);
+ // No images and no left column.
+ expect(component.find('img').exists()).toEqual(false);
+ expect(component.find('div.left-column').exists()).toEqual(false);
+
+ });
+
+ test('Mute Step with side image ', () => {
+ const component = mountWithIntl(
+
+ );
+ expect(component.find('div.mute-step').exists()).toEqual(true);
+ expect(component.find('div.mute-header').exists()).toEqual(true);
+ expect(component.find('div.mute-right-column').exists()).toEqual(true);
+ expect(component.find('div.left-column').exists()).toEqual(true);
+ expect(component.find('img.side-img').exists()).toEqual(true);
+
+ });
+
+ test('Mute Step with bottom image ', () => {
+ const component = mountWithIntl(
+
+ );
+ expect(component.find('div.mute-step').exists()).toEqual(true);
+ expect(component.find('div.mute-header').exists()).toEqual(true);
+ expect(component.find('div.mute-right-column').exists()).toEqual(true);
+ expect(component.find('img.bottom-image').exists()).toEqual(true);
+ });
+});