Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor ReportAbuseButton to use DismissibleTextForm #7382

Merged
merged 3 commits into from
Jan 18, 2019
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
75 changes: 15 additions & 60 deletions src/amo/components/ReportAbuseButton/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,21 @@ import makeClassName from 'classnames';
import { oneLine } from 'common-tags';
import * as React from 'react';
import { connect } from 'react-redux';
import Textarea from 'react-textarea-autosize';
import { compose } from 'redux';

import { withErrorHandler } from 'core/errorHandler';
import type { ErrorHandlerType } from 'core/errorHandler';
import translate from 'core/i18n/translate';
import log from 'core/logger';
import {
disableAbuseButtonUI,
enableAbuseButtonUI,
hideAddonAbuseReportUI,
sendAddonAbuseReport,
showAddonAbuseReportUI,
} from 'core/reducers/abuse';
import { sanitizeHTML } from 'core/utils';
import { normalizeFileNameId, sanitizeHTML } from 'core/utils';
import Button from 'ui/components/Button';
import DismissibleTextForm from 'ui/components/DismissibleTextForm';
import type { OnSubmitParams } from 'ui/components/DismissibleTextForm';
import type { AppState } from 'amo/store';
import type { AddonAbuseState } from 'core/reducers/abuse';
import type { DispatchFunc } from 'core/types/redux';
Expand All @@ -41,11 +40,7 @@ type InternalProps = {|
|};

export class ReportAbuseButtonBase extends React.Component<InternalProps> {
textarea: React.ElementRef<typeof Textarea>;

dismissReportUI = (event: SyntheticEvent<any>) => {
event.preventDefault();

dismissReportUI = () => {
const { addon, dispatch, loading } = this.props;

if (loading) {
Expand All @@ -58,12 +53,10 @@ export class ReportAbuseButtonBase extends React.Component<InternalProps> {
dispatch(hideAddonAbuseReportUI({ addon }));
};

sendReport = (event: SyntheticEvent<any>) => {
event.preventDefault();

sendReport = ({ text }: OnSubmitParams) => {
// The button isn't clickable if there is no content, but just in case:
// we verify there's a message to send.
if (!this.textarea.value.length) {
if (!text.trim().length) {
log.debug(oneLine`User managed to click submit button while textarea
was empty. Ignoring this onClick/sendReport event.`);
return;
Expand All @@ -75,7 +68,7 @@ export class ReportAbuseButtonBase extends React.Component<InternalProps> {
sendAddonAbuseReport({
addonSlug: addon.slug,
errorHandlerId: errorHandler.id,
message: this.textarea.value,
message: text,
}),
);
};
Expand All @@ -86,20 +79,6 @@ export class ReportAbuseButtonBase extends React.Component<InternalProps> {
const { addon, dispatch } = this.props;

dispatch(showAddonAbuseReportUI({ addon }));
this.textarea.focus();
};

textareaChange = () => {
const { abuseReport, addon, dispatch } = this.props;

// Don't dispatch the UI update if the button is already visible.
// We also test for `value.trim()` so the user can't submit an
// empty report full of spaces.
if (this.textarea.value.trim().length && !abuseReport.buttonEnabled) {
dispatch(enableAbuseButtonUI({ addon }));
} else if (!this.textarea.value.trim().length) {
dispatch(disableAbuseButtonUI({ addon }));
}
};

render() {
Expand Down Expand Up @@ -133,8 +112,6 @@ export class ReportAbuseButtonBase extends React.Component<InternalProps> {
);
}

const sendButtonIsDisabled = loading || !abuseReport.buttonEnabled;

const prefaceText = i18n.sprintf(
i18n.gettext(
`If you think this add-on violates
Expand Down Expand Up @@ -190,40 +167,18 @@ export class ReportAbuseButtonBase extends React.Component<InternalProps> {

{errorHandler.renderErrorIfPresent()}

<Textarea
className="ReportAbuseButton-textarea"
disabled={loading}
inputRef={(ref) => {
this.textarea = ref;
}}
onChange={this.textareaChange}
<DismissibleTextForm
id={normalizeFileNameId(__filename)}
isSubmitting={loading}
onSubmit={this.sendReport}
submitButtonText={i18n.gettext('Send abuse report')}
submitButtonInProgressText={i18n.gettext('Sending abuse report')}
onDismiss={this.dismissReportUI}
dismissButtonText={i18n.gettext('Dismiss')}
placeholder={i18n.gettext(
'Explain how this add-on is violating our policies.',
)}
/>

<div className="ReportAbuseButton-buttons">
<a
className={makeClassName('ReportAbuseButton-dismiss-report', {
'ReportAbuseButton-dismiss-report--disabled': loading,
})}
href="#cancel"
onClick={this.dismissReportUI}
>
{i18n.gettext('Dismiss')}
</a>
<Button
buttonType="alert"
className="ReportAbuseButton-send-report"
disabled={sendButtonIsDisabled}
onClick={this.sendReport}
micro
>
{loading
? i18n.gettext('Sending abuse report')
: i18n.gettext('Send abuse report')}
</Button>
</div>
</div>
</div>
);
Expand Down
29 changes: 0 additions & 29 deletions src/amo/components/ReportAbuseButton/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -32,32 +32,3 @@
.ReportAbuseButton-show-more {
width: 100%;
}

.ReportAbuseButton-textarea {
font-size: $font-size-m-smaller;
line-height: 1.4;
margin: 5px auto;
// This is the height of two lines of text. Often the placeholder text
// will span two lines–because this element will grow/shrink based on
// the contents of the text inside it, we set a minimum height so it
// won't shrink when the two-line placeholder is replaced with a single
// character when the user starts typing.
min-height: 51px;
padding: 5px;
resize: none;
width: 100%;
}

.ReportAbuseButton-buttons {
display: flex;
justify-content: space-between;
}

.ReportAbuseButton-dismiss-report {
align-self: center;
}

.ReportAbuseButton-dismiss-report--disabled:link {
color: $neutral-base-color;
cursor: not-allowed;
}
64 changes: 0 additions & 64 deletions src/core/reducers/abuse.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@ import invariant from 'invariant';
import type { AddonType } from 'core/types/addons';
import type { AbuseReporter } from 'core/api/abuse';

export const DISABLE_ADDON_ABUSE_BUTTON_UI: 'DISABLE_ADDON_ABUSE_BUTTON_UI' =
'DISABLE_ADDON_ABUSE_BUTTON_UI';
export const ENABLE_ADDON_ABUSE_BUTTON_UI: 'ENABLE_ADDON_ABUSE_BUTTON_UI' =
'ENABLE_ADDON_ABUSE_BUTTON_UI';
export const HIDE_ADDON_ABUSE_REPORT_UI: 'HIDE_ADDON_ABUSE_REPORT_UI' =
'HIDE_ADDON_ABUSE_REPORT_UI';
export const LOAD_ADDON_ABUSE_REPORT: 'LOAD_ADDON_ABUSE_REPORT' =
Expand Down Expand Up @@ -36,42 +32,6 @@ export const initialState: AbuseState = {
loading: false,
};

type DisableAbuseButtonUIParams = {| addon: AddonType |};

type DisableAbuseButtonUIAction = {|
type: typeof DISABLE_ADDON_ABUSE_BUTTON_UI,
payload: DisableAbuseButtonUIParams,
|};

export function disableAbuseButtonUI({
addon,
}: DisableAbuseButtonUIParams): DisableAbuseButtonUIAction {
invariant(addon, 'addon is required');

return {
type: DISABLE_ADDON_ABUSE_BUTTON_UI,
payload: { addon },
};
}

type EnableAbuseButtonUIParams = {| addon: AddonType |};

type EnableAbuseButtonUIAction = {|
type: typeof ENABLE_ADDON_ABUSE_BUTTON_UI,
payload: EnableAbuseButtonUIParams,
|};

export function enableAbuseButtonUI({
addon,
}: EnableAbuseButtonUIParams): EnableAbuseButtonUIAction {
invariant(addon, 'addon is required');

return {
type: ENABLE_ADDON_ABUSE_BUTTON_UI,
payload: { addon },
};
}

type HideAddonAbuseReportUIParams = {| addon: AddonType |};

type HideAddonAbuseReportUIAction = {|
Expand Down Expand Up @@ -165,8 +125,6 @@ export function showAddonAbuseReportUI({
}

type Action =
| DisableAbuseButtonUIAction
| EnableAbuseButtonUIAction
| HideAddonAbuseReportUIAction
| LoadAddonAbuseReportAction
| SendAddonAbuseReportAction
Expand All @@ -177,28 +135,6 @@ export default function abuseReducer(
action: Action,
) {
switch (action.type) {
case DISABLE_ADDON_ABUSE_BUTTON_UI: {
const { addon } = action.payload;

return {
...state,
bySlug: {
...state.bySlug,
[addon.slug]: { ...state.bySlug[addon.slug], buttonEnabled: false },
},
};
}
case ENABLE_ADDON_ABUSE_BUTTON_UI: {
const { addon } = action.payload;

return {
...state,
bySlug: {
...state.bySlug,
[addon.slug]: { ...state.bySlug[addon.slug], buttonEnabled: true },
},
};
}
case HIDE_ADDON_ABUSE_REPORT_UI: {
const { addon } = action.payload;

Expand Down
Loading