<commit_before>/* @flow */
import makeClassName from 'classnames';
import invariant from 'invariant';
import * as React from 'react';
import { compose } from 'redux';

import { withFixedErrorHandler } from 'amo/errorHandler';
import { getAddonIconUrl } from 'amo/imageUtils';
import translate from 'amo/i18n/translate';
import withUIState from 'amo/withUIState';
import { nl2br, normalizeFileNameId, sanitizeHTML } from 'amo/utils';
import Button from 'amo/components/Button';
import DismissibleTextForm from 'amo/components/DismissibleTextForm';
import Icon from 'amo/components/Icon';
import type {
  DeleteAddonNoteFunc,
  RemoveCollectionAddonFunc,
  SaveAddonNoteFunc,
} from 'amo/pages/Collection';
import type { ErrorHandlerType } from 'amo/types/errorHandler';
import type { AddonType, CollectionAddonType } from 'amo/types/addons';
import type { ElementEvent, HTMLElementEventHandler } from 'amo/types/dom';
import type { I18nType } from 'amo/types/i18n';
import type { OnSubmitParams } from 'amo/components/DismissibleTextForm';

import './styles.scss';

type Props = {|
  addon: AddonType | CollectionAddonType,
  // TODO: Remove this as it isn't used and therefore cannot be tested.
  // See https://github.com/mozilla/addons-frontend/issues/11392.
  className?: string,
  deleteNote: DeleteAddonNoteFunc,
  removeAddon: RemoveCollectionAddonFunc,
  saveNote: SaveAddonNoteFunc,
|};

type UIStateType = {|
  editingNote: boolean,
|};

const initialUIState: UIStateType = { editingNote: false };

type InternalProps = {|
  ...Props,
  errorHandler: ErrorHandlerType,
  i18n: I18nType,
  setUIState: ($Shape<UIStateType>) => void,
  uiState: UIStateType,
|};

export const extractId = (ownProps: Props | InternalProps): string => {
  const { addon } = ownProps;
  return `editable-collection-addon-${addon.id}`;
};

export class EditableCollectionAddonBase extends React.Component<InternalProps> {
  onEditNote: HTMLElementEventHandler = (event: ElementEvent) => {
    event.preventDefault();
    this.props.setUIState({ editingNote: true });
  };

  onDeleteNote: () => void = () => {
    const {
      addon: { id: addonId },
      deleteNote,
      errorHandler,
    } = this.props;

    deleteNote(addonId, errorHandler);
  };

  onDismissNoteForm: () => void = () => {
    this.props.setUIState({ editingNote: false });
  };

  onRemoveAddon: HTMLElementEventHandler = (event: ElementEvent) => {
    const {
      addon: { id: addonId },
      removeAddon,
    } = this.props;

    event.preventDefault();
    event.stopPropagation();

    invariant(addonId, 'addonId is required');

    removeAddon(addonId);
  };

  onSaveNote: (notes: OnSubmitParams) => void = (notes: OnSubmitParams) => {
    const {
      addon: { id: addonId },
      errorHandler,
      saveNote,
    } = this.props;

    invariant(addonId, 'addonId is required');

    saveNote(addonId, errorHandler, notes.text);
  };

  render(): React.Node {
    const { addon, className, errorHandler, i18n } = this.props;
    const showNotes = addon.notes || this.props.uiState.editingNote;
    const iconURL = getAddonIconUrl(addon);

    return (
      <li
        className={makeClassName(
          'EditableCollectionAddon',
          `EditableCollectionAddon--${addon.type}`,
          className,
        )}
      >
        <div className="EditableCollectionAddon-details">
          <img
            className="EditableCollectionAddon-icon"
            src={iconURL}
            alt={addon.name}
          />
          <h2 className="EditableCollectionAddon-name">{addon.name}</h2>
        </div>
        <div className="EditableCollectionAddon-buttons">
          <div
            className={makeClassName('EditableCollectionAddon-leaveNote', {
              'EditableCollectionAddon-leaveNote--hidden': showNotes,
            })}
          >
            <Button
              buttonType="action"
              className="EditableCollectionAddon-leaveNote-button"
              micro
              onClick={this.onEditNote}
            >
              {i18n.gettext('Leave a note')}
            </Button>
          </div>
          <Button<commit_msg>Remove className prop from EditableCollectionAddon (#11436)<commit_after>/* @flow */
import makeClassName from 'classnames';
import invariant from 'invariant';
import * as React from 'react';
import { compose } from 'redux';

import { withFixedErrorHandler } from 'amo/errorHandler';
import { getAddonIconUrl } from 'amo/imageUtils';
import translate from 'amo/i18n/translate';
import withUIState from 'amo/withUIState';
import { nl2br, normalizeFileNameId, sanitizeHTML } from 'amo/utils';
import Button from 'amo/components/Button';
import DismissibleTextForm from 'amo/components/DismissibleTextForm';
import Icon from 'amo/components/Icon';
import type {
  DeleteAddonNoteFunc,
  RemoveCollectionAddonFunc,
  SaveAddonNoteFunc,
} from 'amo/pages/Collection';
import type { ErrorHandlerType } from 'amo/types/errorHandler';
import type { AddonType, CollectionAddonType } from 'amo/types/addons';
import type { ElementEvent, HTMLElementEventHandler } from 'amo/types/dom';
import type { I18nType } from 'amo/types/i18n';
import type { OnSubmitParams } from 'amo/components/DismissibleTextForm';

import './styles.scss';

type Props = {|
  addon: AddonType | CollectionAddonType,
  deleteNote: DeleteAddonNoteFunc,
  removeAddon: RemoveCollectionAddonFunc,
  saveNote: SaveAddonNoteFunc,
|};

type UIStateType = {|
  editingNote: boolean,
|};

const initialUIState: UIStateType = { editingNote: false };

type InternalProps = {|
  ...Props,
  errorHandler: ErrorHandlerType,
  i18n: I18nType,
  setUIState: ($Shape<UIStateType>) => void,
  uiState: UIStateType,
|};

export const extractId = (ownProps: Props | InternalProps): string => {
  const { addon } = ownProps;
  return `editable-collection-addon-${addon.id}`;
};

export class EditableCollectionAddonBase extends React.Component<InternalProps> {
  onEditNote: HTMLElementEventHandler = (event: ElementEvent) => {
    event.preventDefault();
    this.props.setUIState({ editingNote: true });
  };

  onDeleteNote: () => void = () => {
    const {
      addon: { id: addonId },
      deleteNote,
      errorHandler,
    } = this.props;

    deleteNote(addonId, errorHandler);
  };

  onDismissNoteForm: () => void = () => {
    this.props.setUIState({ editingNote: false });
  };

  onRemoveAddon: HTMLElementEventHandler = (event: ElementEvent) => {
    const {
      addon: { id: addonId },
      removeAddon,
    } = this.props;

    event.preventDefault();
    event.stopPropagation();

    invariant(addonId, 'addonId is required');

    removeAddon(addonId);
  };

  onSaveNote: (notes: OnSubmitParams) => void = (notes: OnSubmitParams) => {
    const {
      addon: { id: addonId },
      errorHandler,
      saveNote,
    } = this.props;

    invariant(addonId, 'addonId is required');

    saveNote(addonId, errorHandler, notes.text);
  };

  render(): React.Node {
    const { addon, errorHandler, i18n } = this.props;
    const showNotes = addon.notes || this.props.uiState.editingNote;
    const iconURL = getAddonIconUrl(addon);

    return (
      <li
        className={makeClassName(
          'EditableCollectionAddon',
          `EditableCollectionAddon--${addon.type}`,
        )}
      >
        <div className="EditableCollectionAddon-details">
          <img
            className="EditableCollectionAddon-icon"
            src={iconURL}
            alt={addon.name}
          />
          <h2 className="EditableCollectionAddon-name">{addon.name}</h2>
        </div>
        <div className="EditableCollectionAddon-buttons">
          <div
            className={makeClassName('EditableCollectionAddon-leaveNote', {
              'EditableCollectionAddon-leaveNote--hidden': showNotes,
            })}
          >
            <Button
              buttonType="action"
              className="EditableCollectionAddon-leaveNote-button"
              micro
              onClick={this.onEditNote}
            >
              {i18n.gettext('Leave a note')}
            </Button>
          </div>
          <Button
{'fraction_non_alphanumeric': 0.12241633858267717, 'fraction_numerical': 0.0054133858267716535, 'mean_word_length': 3.1772867420349433, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 49, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2975128', 'n_tokens_mistral': 2485, 'n_tokens_neox': 2446, 'n_words': 511}