Move default props to default parameters - simple cases

React throws a deprecation warning for defaultProps.
This commit refactors default props to default values in easy-to-fix cases.

[MAILPOET-6108]
This commit is contained in:
Rostislav Wolny
2024-06-13 16:57:04 +02:00
committed by Jan Lysý
parent b19c30d203
commit a3514377c8
59 changed files with 438 additions and 669 deletions

View File

@ -4,25 +4,31 @@ import ReactDOMServer from 'react-dom/server';
import { MailPoet } from 'mailpoet';
function ConfirmAlert(props) {
function ConfirmAlert({
message,
onConfirm,
title = __('Confirm to proceed', 'mailpoet'),
cancelLabel = __('Cancel', 'mailpoet'),
confirmLabel = __('Confirm', 'mailpoet'),
}) {
MailPoet.Modal.popup({
title: props.title,
title,
template: ReactDOMServer.renderToString(
<>
<p>{props.message}</p>
<p>{message}</p>
<button
id="mailpoet_alert_cancel"
className="button button-secondary"
type="button"
>
{props.cancelLabel}
{cancelLabel}
</button>
<button
id="mailpoet_alert_confirm"
className="button button-primary"
type="submit"
>
{props.confirmLabel}
{confirmLabel}
</button>
</>,
),
@ -31,7 +37,7 @@ function ConfirmAlert(props) {
.getElementById('mailpoet_alert_confirm')
.addEventListener('click', () => {
MailPoet.Modal.close();
props.onConfirm();
onConfirm();
});
document
@ -50,12 +56,6 @@ ConfirmAlert.propTypes = {
onConfirm: PropTypes.func.isRequired,
};
ConfirmAlert.defaultProps = {
title: __('Confirm to proceed', 'mailpoet'),
cancelLabel: __('Cancel', 'mailpoet'),
confirmLabel: __('Confirm', 'mailpoet'),
};
export function confirmAlert(props) {
// the below render is only to invoke proptypes on ConfirmAlert
ReactDOMServer.renderToString(

View File

@ -1,10 +1,10 @@
import PropTypes from 'prop-types';
function KeyValueTable(props) {
function KeyValueTable({ rows, max_width: maxWidth = 'auto' }) {
return (
<table className="widefat fixed" style={{ maxWidth: props.max_width }}>
<table className="widefat fixed" style={{ maxWidth }}>
<tbody>
{props.rows.map((row) => (
{rows.map((row) => (
<tr key={`row_${row.key}`}>
<td className="row-title">{row.key}</td>
<td>{row.value}</td>
@ -29,8 +29,4 @@ KeyValueTable.propTypes = {
).isRequired,
};
KeyValueTable.defaultProps = {
max_width: 'auto',
};
export { KeyValueTable };

View File

@ -18,7 +18,7 @@ function Badge({
tooltipId,
tooltipPlace,
type,
isInverted,
isInverted = true,
}: BadgeProps) {
return (
<span>
@ -38,8 +38,4 @@ function Badge({
);
}
Badge.defaultProps = {
isInverted: true,
};
export { Badge };

View File

@ -69,6 +69,7 @@ export const getBadgeType = (statName, rate) => {
};
function StatsBadge(props: StatsBadgeProps) {
const { isInverted = true } = props;
const badges = {
excellent: {
name: __('Excellent', 'mailpoet'),
@ -119,7 +120,7 @@ function StatsBadge(props: StatsBadgeProps) {
const content = (
<Badge
isInverted={props.isInverted}
isInverted={isInverted}
type={badgeType}
name={badge.name}
tooltip={tooltipText}
@ -131,8 +132,4 @@ function StatsBadge(props: StatsBadgeProps) {
return content;
}
StatsBadge.defaultProps = {
isInverted: true,
};
export { StatsBadge };

View File

@ -20,7 +20,7 @@ function MssActiveMessage({ canUseSuccessClass }: MssActiveMessageProps) {
type NotValidMessageProps = { message?: string };
function NotValidMessage({ message }: NotValidMessageProps) {
function NotValidMessage({ message = '' }: NotValidMessageProps) {
return (
<div className="mailpoet_error">
{message
@ -45,10 +45,6 @@ function NotValidMessage({ message }: NotValidMessageProps) {
);
}
NotValidMessage.defaultProps = {
message: '',
};
type MssNotActiveMessageProps = { activationCallback?: () => void };
function MssNotActiveMessage({ activationCallback }: MssNotActiveMessageProps) {
@ -74,19 +70,21 @@ type Props = {
canUseSuccessClass: boolean;
};
export function MssMessages(props: Props) {
export function MssMessages({
activationCallback,
canUseSuccessClass,
keyMessage = '',
}: Props) {
const { mssStatus, mssAccessRestriction } = useSelector(
'getKeyActivationState',
)();
switch (mssStatus) {
case MssStatus.VALID_MSS_ACTIVE:
return <MssActiveMessage canUseSuccessClass={props.canUseSuccessClass} />;
return <MssActiveMessage canUseSuccessClass={canUseSuccessClass} />;
case MssStatus.VALID_MSS_NOT_ACTIVE:
return (
<MssNotActiveMessage activationCallback={props.activationCallback} />
);
return <MssNotActiveMessage activationCallback={activationCallback} />;
case MssStatus.INVALID:
return <NotValidMessage message={props.keyMessage} />;
return <NotValidMessage message={keyMessage} />;
case MssStatus.VALID_UNDERPRIVILEGED:
if (
@ -101,7 +99,3 @@ export function MssMessages(props: Props) {
return null;
}
}
MssMessages.defaultProps = {
keyMessage: '',
};

View File

@ -65,7 +65,7 @@ function PremiumMessageWithModal(props: PremiumMessageProps) {
type NotValidMessageProps = { message?: string };
function NotValidMessage({ message }: NotValidMessageProps) {
function NotValidMessage({ message = '' }: NotValidMessageProps) {
return (
<div className="mailpoet_error">
{message
@ -87,21 +87,17 @@ function NotValidMessage({ message }: NotValidMessageProps) {
);
}
NotValidMessage.defaultProps = {
message: '',
};
type Props = {
keyMessage?: string;
canUseSuccessClass: boolean;
};
function PremiumMessages(props: Props) {
function PremiumMessages({ canUseSuccessClass, keyMessage = '' }: Props) {
const { premiumStatus: status } = useSelector('getKeyActivationState')();
switch (status) {
case PremiumStatus.VALID_PREMIUM_PLUGIN_ACTIVE:
return <ActiveMessage canUseSuccessClass={props.canUseSuccessClass} />;
return <ActiveMessage canUseSuccessClass={canUseSuccessClass} />;
case PremiumStatus.VALID_PREMIUM_PLUGIN_NOT_INSTALLED:
return (
<PremiumMessageWithModal
@ -132,14 +128,10 @@ function PremiumMessages(props: Props) {
/>
);
case PremiumStatus.INVALID:
return <NotValidMessage message={props.keyMessage} />;
return <NotValidMessage message={keyMessage} />;
default:
return null;
}
}
PremiumMessages.defaultProps = {
keyMessage: '',
};
export { PremiumMessages, PremiumMessageWithModal };

View File

@ -5,7 +5,11 @@ import classnames from 'classnames';
import { MobileIcon } from './mobile-icon';
import { DesktopIcon } from './desktop-icon';
function Preview({ children, onDisplayTypeChange, selectedDisplayType }) {
function Preview({
children,
onDisplayTypeChange = (type) => type,
selectedDisplayType = 'desktop',
}) {
const [displayType, setDisplayType] = useState(selectedDisplayType);
const changeType = (type) => {
setDisplayType(type);
@ -76,9 +80,5 @@ Preview.propTypes = {
selectedDisplayType: PropTypes.string,
};
Preview.defaultProps = {
onDisplayTypeChange: () => {},
selectedDisplayType: 'desktop',
};
Preview.displayName = 'FormEditorPreview';
export { Preview };

View File

@ -1,12 +1,17 @@
import PropTypes from 'prop-types';
import { __ } from '@wordpress/i18n';
function PrintBoolean(props) {
function PrintBoolean({
truthy = __('Yes', 'mailpoet'),
falsy = __('No', 'mailpoet'),
unknown = __('Unknown', 'mailpoet'),
children = null,
}) {
return (
<span>
{(props.children === true && props.truthy) ||
(props.children === false && props.falsy) ||
props.unknown}
{(children === true && truthy) ||
(children === false && falsy) ||
unknown}
</span>
);
}
@ -18,11 +23,4 @@ PrintBoolean.propTypes = {
children: PropTypes.bool,
};
PrintBoolean.defaultProps = {
truthy: __('Yes', 'mailpoet'),
falsy: __('No', 'mailpoet'),
unknown: __('Unknown', 'mailpoet'),
children: null,
};
export { PrintBoolean };

View File

@ -88,7 +88,10 @@ type Props = {
setAuthorizedAddress?: (address: string) => void;
};
function SetFromAddressModal({ onRequestClose, setAuthorizedAddress }: Props) {
function SetFromAddressModal({
onRequestClose,
setAuthorizedAddress = noop,
}: Props) {
const [address, setAddress] = useState<string>();
const [showAuthorizedEmailModal, setShowAuthorizedEmailModal] =
useState(false);
@ -230,8 +233,4 @@ function SetFromAddressModal({ onRequestClose, setAuthorizedAddress }: Props) {
);
}
SetFromAddressModal.defaultProps = {
setAuthorizedAddress: noop,
};
export { SetFromAddressModal };

View File

@ -10,7 +10,7 @@ type Props = {
doneCallback?: (step: string) => void;
};
function StepsComponent({ count, current, titles, doneCallback }: Props) {
function StepsComponent({ count, current, doneCallback, titles = [] }: Props) {
return (
<div className="mailpoet-steps">
<ContentWrapperFix />
@ -54,9 +54,6 @@ function StepsComponent({ count, current, titles, doneCallback }: Props) {
);
}
StepsComponent.defaultProps = {
titles: [],
};
StepsComponent.displayName = 'StepsComponent';
const Steps = withBoundary(StepsComponent);
export { Steps };

View File

@ -12,15 +12,15 @@ import { MailPoet } from 'mailpoet';
import { CustomFieldDelete } from '../custom-field-delete.jsx';
function CustomFieldSettings({
label,
mandatory,
isSaving,
onSave,
isChecked,
checkboxLabel,
isDeleting,
onCustomFieldDelete,
onChange,
label = '',
mandatory = false,
isSaving = false,
onSave = null,
isChecked = false,
checkboxLabel = '',
isDeleting = false,
onCustomFieldDelete = null,
onChange = () => {},
}) {
const [localLabel, setLocalLabel] = useState(label);
const [localMandatory, setLocalMandatory] = useState(mandatory);
@ -108,16 +108,4 @@ CustomFieldSettings.propTypes = {
onChange: PropTypes.func,
};
CustomFieldSettings.defaultProps = {
label: '',
mandatory: false,
onSave: null,
isSaving: false,
isChecked: false,
checkboxLabel: '',
isDeleting: false,
onCustomFieldDelete: null,
onChange: () => {},
};
export { CustomFieldSettings };

View File

@ -11,17 +11,17 @@ import { MailPoet } from 'mailpoet';
import { CustomFieldDelete } from '../custom-field-delete.jsx';
function CustomFieldSettings({
label,
mandatory,
dateType,
dateFormat,
defaultToday,
dateSettings,
isSaving,
onSave,
isDeleting,
onCustomFieldDelete,
onChange,
label = '',
mandatory = false,
dateType = null,
dateFormat = null,
defaultToday = false,
isSaving = false,
onSave = null,
isDeleting = false,
onCustomFieldDelete = null,
onChange = null,
}) {
const [localLabel, setLocalLabel] = useState(label);
const [localMandatory, setLocalMandatory] = useState(mandatory);
@ -161,17 +161,4 @@ CustomFieldSettings.propTypes = {
onChange: PropTypes.func,
};
CustomFieldSettings.defaultProps = {
label: '',
mandatory: false,
isSaving: false,
dateType: null,
dateFormat: null,
defaultToday: false,
isDeleting: false,
onCustomFieldDelete: null,
onSave: null,
onChange: null,
};
export { CustomFieldSettings };

View File

@ -236,12 +236,13 @@ class FormFieldDate extends Component {
}
render() {
const { field, addDefaultClasses = false } = this.props;
const monthNames = window.mailpoet_month_names || [];
const dateFormats = window.mailpoet_date_formats || {};
const dateType = this.props.field.params.date_type;
const dateType = field.params.date_type;
let dateFormat = dateFormats[dateType][0];
if (this.props.field.params.date_format) {
dateFormat = this.props.field.params.date_format;
if (field.params.date_format) {
dateFormat = field.params.date_format;
}
const dateSelects = dateFormat.split('/');
@ -252,10 +253,10 @@ class FormFieldDate extends Component {
<FormFieldDateYear
onValueChange={this.onValueChange}
key="year"
name={this.props.field.name}
addDefaultClasses={this.props.addDefaultClasses}
name={field.name}
addDefaultClasses={addDefaultClasses}
year={this.state.year}
placeholder={this.props.field.year_placeholder}
placeholder={field.year_placeholder}
/>
);
@ -264,11 +265,11 @@ class FormFieldDate extends Component {
<FormFieldDateMonth
onValueChange={this.onValueChange}
key="month"
name={this.props.field.name}
addDefaultClasses={this.props.addDefaultClasses}
name={field.name}
addDefaultClasses={addDefaultClasses}
month={this.state.month}
monthNames={monthNames}
placeholder={this.props.field.month_placeholder}
placeholder={field.month_placeholder}
/>
);
@ -277,10 +278,10 @@ class FormFieldDate extends Component {
<FormFieldDateDay
onValueChange={this.onValueChange}
key="day"
name={this.props.field.name}
addDefaultClasses={this.props.addDefaultClasses}
name={field.name}
addDefaultClasses={addDefaultClasses}
day={this.state.day}
placeholder={this.props.field.day_placeholder}
placeholder={field.day_placeholder}
/>
);
@ -306,8 +307,4 @@ FormFieldDate.propTypes = {
addDefaultClasses: PropTypes.bool,
};
FormFieldDate.defaultProps = {
addDefaultClasses: false,
};
export { FormFieldDate };

View File

@ -3,7 +3,7 @@ import { Button } from '@wordpress/components';
import PropTypes from 'prop-types';
import { MailPoet } from 'mailpoet';
function CustomFieldDelete({ isBusy, onDelete }) {
function CustomFieldDelete({ isBusy = false, onDelete = () => {} }) {
const displayConfirm = useCallback(() => {
const result = window.confirm(MailPoet.I18n.t('customFieldDeleteConfirm')); // eslint-disable-line no-alert
if (result) {
@ -29,9 +29,4 @@ CustomFieldDelete.propTypes = {
onDelete: PropTypes.func,
};
CustomFieldDelete.defaultProps = {
isBusy: false,
onDelete: () => {},
};
export { CustomFieldDelete };

View File

@ -8,15 +8,15 @@ import { CustomFieldDelete } from '../custom-field-delete.jsx';
import { Preview } from './settings-preview.jsx';
function CustomFieldSettings({
label,
mandatory,
values,
isSaving,
onSave,
isDeleting,
onCustomFieldDelete,
onChange,
useDragAndDrop,
label = '',
mandatory = false,
values = [],
isSaving = false,
onSave = null,
isDeleting = false,
onCustomFieldDelete = null,
onChange = null,
useDragAndDrop = true,
}) {
const [localLabel, setLocalLabel] = useState(label);
const [localMandatory, setLocalMandatory] = useState(mandatory);
@ -149,16 +149,4 @@ CustomFieldSettings.propTypes = {
useDragAndDrop: PropTypes.bool,
};
CustomFieldSettings.defaultProps = {
label: '',
mandatory: false,
isSaving: false,
values: [],
isDeleting: false,
onCustomFieldDelete: null,
onSave: null,
onChange: null,
useDragAndDrop: true,
};
export { CustomFieldSettings };

View File

@ -11,15 +11,15 @@ import { MailPoet } from 'mailpoet';
import { CustomFieldDelete } from '../custom-field-delete.jsx';
function CustomFieldSettings({
label,
mandatory,
validate,
isSaving,
onSave,
isDeleting,
onCustomFieldDelete,
onChange,
fieldType,
label = '',
mandatory = false,
validate = '',
isSaving = false,
onSave = null,
isDeleting = false,
onCustomFieldDelete = null,
onChange = null,
fieldType = '',
}) {
const [localLabel, setLocalLabel] = useState(label);
const [localMandatory, setLocalMandatory] = useState(mandatory);
@ -116,16 +116,4 @@ CustomFieldSettings.propTypes = {
fieldType: PropTypes.string,
};
CustomFieldSettings.defaultProps = {
label: '',
mandatory: false,
fieldType: '',
isSaving: false,
validate: '',
isDeleting: false,
onCustomFieldDelete: null,
onSave: null,
onChange: null,
};
export { CustomFieldSettings };

View File

@ -1,7 +1,7 @@
import PropTypes from 'prop-types';
import classnames from 'classnames';
function ParagraphEdit({ children, className }) {
function ParagraphEdit({ children, className = '' }) {
return (
<div className={classnames('mailpoet_paragraph', className)}>
{children}
@ -14,8 +14,4 @@ ParagraphEdit.propTypes = {
className: PropTypes.string,
};
ParagraphEdit.defaultProps = {
className: '',
};
export { ParagraphEdit };

View File

@ -14,7 +14,7 @@ function TextInputEdit({
name,
mandatory,
styles,
className,
className = '',
}) {
const settings = useSelect(
(select) => select(storeName).getFormSettings(),
@ -125,8 +125,4 @@ TextInputEdit.propTypes = {
styles: inputStylesPropTypes.isRequired,
};
TextInputEdit.defaultProps = {
className: '',
};
export { TextInputEdit };

View File

@ -3,7 +3,17 @@ import codemirror from 'codemirror';
import 'codemirror/mode/css/css'; // Side effect
import PropTypes from 'prop-types';
function CodemirrorWrap({ options, value, onChange }) {
function CodemirrorWrap({
value,
onChange,
options = {
lineNumbers: true,
tabMode: 'indent',
matchBrackets: true,
theme: 'neo',
mode: 'css',
},
}) {
const textArea = useRef(null);
const editor = useRef(null);
@ -56,14 +66,4 @@ CodemirrorWrap.propTypes = {
}),
};
CodemirrorWrap.defaultProps = {
options: {
lineNumbers: true,
tabMode: 'indent',
matchBrackets: true,
theme: 'neo',
mode: 'css',
},
};
export { CodemirrorWrap };

View File

@ -12,7 +12,7 @@ function FormPlacementOption({
label,
icon,
active,
canBeActive,
canBeActive = true,
onClick,
}: Props): JSX.Element {
return (
@ -32,8 +32,4 @@ function FormPlacementOption({
);
}
FormPlacementOption.defaultProps = {
canBeActive: true,
};
export { FormPlacementOption };

View File

@ -18,12 +18,12 @@ type Props = {
function SelectionItem({
label,
active,
canBeActive,
onClick,
children,
className,
automationId,
displaySettingsIcon,
canBeActive = true,
className = undefined,
automationId = undefined,
displaySettingsIcon = true,
}: Props): JSX.Element {
const [hover, setHover] = useState(false);
return (
@ -75,11 +75,4 @@ function SelectionItem({
);
}
SelectionItem.defaultProps = {
canBeActive: true,
displaySettingsIcon: true,
className: undefined,
automationId: undefined,
};
export { SelectionItem };

View File

@ -180,6 +180,7 @@ class FormField extends Component {
render() {
let field = false;
const { onValueChange = () => {} } = this.props;
if (this.props.field.fields !== undefined) {
field = this.props.field.fields.map((subfield, index) =>
@ -187,7 +188,7 @@ class FormField extends Component {
index,
field: subfield,
item: this.props.item,
onValueChange: this.props.onValueChange || false,
onValueChange: onValueChange || false,
}),
);
} else {
@ -237,10 +238,4 @@ FormField.propTypes = {
item: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
};
FormField.defaultProps = {
onValueChange: function onValueChange() {
// no-op
},
};
export { FormField };

View File

@ -9,7 +9,10 @@ class FormFieldRadio extends Component {
this.onValueChange = this.onValueChange.bind(this);
}
onValueChange = (value, e) => this.props.onValueChange(e);
onValueChange = (value, e) => {
const { onValueChange = () => {} } = this.props.onValueChange;
onValueChange(e);
};
render() {
if (this.props.field.values === undefined) {
@ -43,10 +46,4 @@ FormFieldRadio.propTypes = {
item: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
};
FormFieldRadio.defaultProps = {
onValueChange: function onValueChange() {
// no-op
},
};
export { FormFieldRadio };

View File

@ -9,6 +9,8 @@ export class FormFieldSelect extends Component {
return false;
}
const { automationId = '', onValueChange = () => {} } = this.props;
let filter = false;
let placeholder = false;
let sortBy = false;
@ -55,8 +57,8 @@ export class FormFieldSelect extends Component {
name={this.props.field.name}
id={`field_${this.props.field.name}`}
value={this.props.item[this.props.field.name] || ''}
onChange={this.props.onValueChange}
automationId={this.props.automationId}
onChange={onValueChange}
automationId={automationId}
{...this.props.field.validation}
>
{placeholder}
@ -85,10 +87,3 @@ FormFieldSelect.propTypes = {
item: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
automationId: PropTypes.string,
};
FormFieldSelect.defaultProps = {
automationId: '',
onValueChange: function onValueChange() {
// no-op
},
};

View File

@ -19,10 +19,13 @@ class Selection extends Component {
}
componentDidUpdate(prevProps) {
const { item = undefined } = this.props;
const { item: prevItem = undefined } = prevProps;
if (
this.props.item !== undefined &&
prevProps.item !== undefined &&
this.props.item.id !== prevProps.item.id
item !== undefined &&
prevItem !== undefined &&
item.id !== prevItem.id
) {
jQuery(`#${this.selectRef.current.id}`)
.val(this.getSelectedValues())
@ -50,16 +53,17 @@ class Selection extends Component {
};
getSelectedValues = () => {
if (this.props.field.selected !== undefined) {
return this.props.field.selected(this.props.item);
const { field, item = undefined } = this.props;
if (field.selected !== undefined) {
return field.selected(item);
}
if (this.props.item !== undefined && this.props.field.name !== undefined) {
if (item !== undefined && field.name !== undefined) {
if (this.allowMultipleValues()) {
if (_.isArray(this.props.item[this.props.field.name])) {
return this.props.item[this.props.field.name].map((item) => item.id);
if (_.isArray(item[field.name])) {
return item[field.name].map((it) => it.id);
}
} else {
return this.props.item[this.props.field.name];
return item[field.name];
}
}
return null;
@ -85,36 +89,41 @@ class Selection extends Component {
};
getLabel = (item) => {
if (this.props.field.getLabel !== undefined) {
return this.props.field.getLabel(item, this.props.item);
const { field, item: propsItem = undefined } = this.props;
if (field.getLabel !== undefined) {
return field.getLabel(item, propsItem);
}
return item.name;
};
getSearchLabel = (item) => {
if (this.props.field.getSearchLabel !== undefined) {
return this.props.field.getSearchLabel(item, this.props.item);
const { field, item: propsItem = undefined } = this.props;
if (field.getSearchLabel !== undefined) {
return field.getSearchLabel(item, propsItem);
}
return null;
};
getValue = (item) => {
if (this.props.field.getValue !== undefined) {
return this.props.field.getValue(item, this.props.item);
const { field, item: propsItem = undefined } = this.props;
if (field.getValue !== undefined) {
return field.getValue(item, propsItem);
}
return item.id;
};
getCount = (item) => {
if (this.props.field.getCount !== undefined) {
return this.props.field.getCount(item, this.props.item);
const { field, item: propsItem = undefined } = this.props;
if (field.getCount !== undefined) {
return field.getCount(item, propsItem);
}
return null;
};
getTag = (item) => {
if (this.props.field.getTag !== undefined) {
return this.props.field.getTag(item, this.props.item);
const { field, item: propsItem = undefined } = this.props;
if (field.getTag !== undefined) {
return field.getTag(item, propsItem);
}
return null;
};
@ -135,9 +144,11 @@ class Selection extends Component {
return tpl;
};
const { width = '', disabled = false } = this.props;
let select2Options = {
disabled: this.props.disabled || false,
width: this.props.width || '',
disabled: disabled || false,
width: width || '',
placeholder: {
id: '', // the value of the option
text: this.props.field.placeholder,
@ -266,7 +277,7 @@ class Selection extends Component {
this.allowMultipleValues() || this.props.field.forceSelect2;
handleChange = (e) => {
if (this.props.onValueChange === undefined) return;
const { onValueChange = () => {} } = this.props;
const valueTextPair = jQuery(`#${this.selectRef.current.id}`)
.children(':selected')
@ -278,7 +289,7 @@ class Selection extends Component {
: _.pluck(valueTextPair, 'id').toString();
const transformedValue = this.transformChangedValue(value, valueTextPair);
this.props.onValueChange({
onValueChange({
target: {
value: transformedValue,
name: this.props.field.name,
@ -390,13 +401,4 @@ Selection.propTypes = {
width: PropTypes.string,
};
Selection.defaultProps = {
onValueChange: function onValueChange() {
// no-op
},
disabled: false,
width: '',
item: undefined,
};
export { Selection };

View File

@ -5,12 +5,13 @@ import { Input } from 'common/form/input/input';
// eslint-disable-next-line react/prefer-stateless-function, max-len
class FormFieldText extends Component {
render() {
const { onValueChange = () => {}, onBlurEvent = () => {} } = this.props;
const name = this.props.field.name || null;
const item = this.props.item || {};
let value;
let defaultValue;
// value should only be set when onChangeValue is configured
if (this.props.onValueChange instanceof Function) {
if (onValueChange instanceof Function) {
value = item[this.props.field.name];
// set value to defaultValue if available
value = value === undefined ? this.props.field.defaultValue || '' : value;
@ -54,8 +55,8 @@ class FormFieldText extends Component {
value={value}
defaultValue={defaultValue}
placeholder={this.props.field.placeholder}
onChange={this.props.onValueChange}
onBlur={this.props.onBlurEvent}
onChange={onValueChange}
onBlur={onBlurEvent}
customLabel={this.props.field.customLabel}
tooltip={this.props.field.tooltip}
{...this.props.field.validation}
@ -88,13 +89,4 @@ FormFieldText.propTypes = {
item: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
};
FormFieldText.defaultProps = {
onValueChange: function onValueChange() {
// no-op
},
onBlurEvent: function onValueChange() {
// no-op
},
};
export { FormFieldText };

View File

@ -56,10 +56,4 @@ Tooltip.propTypes = {
className: PropTypes.string,
};
Tooltip.defaultProps = {
tooltipId: undefined,
place: undefined,
className: undefined,
};
export { Tooltip };

View File

@ -2,39 +2,38 @@ import PropTypes from 'prop-types';
import { MailPoet } from 'mailpoet';
import parseDate from 'date-fns/parse';
function TasksListDataRow(props) {
let scheduled = props.task.scheduled_at;
function TasksListDataRow({
task,
show_scheduled_at: showScheduledAt = false,
}) {
let scheduled = task.scheduled_at;
if (scheduled) {
scheduled = parseDate(scheduled, 'yyyy-MM-dd HH:mm:ss', new Date());
}
const updated = parseDate(
props.task.updated_at,
'yyyy-MM-dd HH:mm:ss',
new Date(),
);
const updated = parseDate(task.updated_at, 'yyyy-MM-dd HH:mm:ss', new Date());
return (
<tr>
<td className="column column-primary">{props.task.id}</td>
<td className="column">{props.task.type}</td>
<td className="column column-primary">{task.id}</td>
<td className="column">{task.type}</td>
<td className="column">
{props.task.newsletter ? (
{task.newsletter ? (
<a
href={props.task.newsletter.preview_url}
data-newsletter-id={props.task.newsletter.newsletter_id}
data-queue-id={props.task.newsletter.queue_id}
href={task.newsletter.preview_url}
data-newsletter-id={task.newsletter.newsletter_id}
data-queue-id={task.newsletter.queue_id}
target="_blank"
rel="noopener noreferrer"
>
{props.task.newsletter.subject || MailPoet.I18n.t('preview')}
{task.newsletter.subject || MailPoet.I18n.t('preview')}
</a>
) : (
MailPoet.I18n.t('none')
)}
</td>
<td className="column">{props.task.priority}</td>
{props.show_scheduled_at ? (
<td className="column">{task.priority}</td>
{showScheduledAt ? (
<td className="column-date">
<abbr>{`${MailPoet.Date.short(scheduled)} ${MailPoet.Date.time(
scheduled,
@ -68,8 +67,4 @@ TasksListDataRow.propTypes = {
}).isRequired,
};
TasksListDataRow.defaultProps = {
show_scheduled_at: false,
};
export { TasksListDataRow };

View File

@ -1,14 +1,14 @@
import PropTypes from 'prop-types';
import { MailPoet } from 'mailpoet';
function TasksListLabelsRow(props) {
function TasksListLabelsRow({ show_scheduled_at: showScheduledAt = false }) {
return (
<tr>
<th className="row-title">Id</th>
<th className="row-title">{MailPoet.I18n.t('type')}</th>
<th className="row-title">{MailPoet.I18n.t('email')}</th>
<th className="row-title">{MailPoet.I18n.t('priority')}</th>
{props.show_scheduled_at ? (
{showScheduledAt ? (
<th className="row-title">{MailPoet.I18n.t('scheduledAt')}</th>
) : null}
<th className="row-title">{MailPoet.I18n.t('updatedAt')}</th>
@ -20,8 +20,4 @@ TasksListLabelsRow.propTypes = {
show_scheduled_at: PropTypes.bool,
};
TasksListLabelsRow.defaultProps = {
show_scheduled_at: false,
};
export { TasksListLabelsRow };

View File

@ -3,21 +3,21 @@ import { MailPoet } from 'mailpoet';
import { TasksListDataRow } from './tasks-list-data-row.jsx';
import { TasksListLabelsRow } from './tasks-list-labels-row.jsx';
function TasksList(props) {
const colsCount = props.show_scheduled_at ? 6 : 5;
function TasksList({ tasks, show_scheduled_at: showScheduledAt = false }) {
const colsCount = showScheduledAt ? 6 : 5;
return (
<table className="widefat fixed striped">
<thead>
<TasksListLabelsRow show_scheduled_at={props.show_scheduled_at} />
<TasksListLabelsRow show_scheduled_at={showScheduledAt} />
</thead>
<tbody>
{props.tasks.length ? (
props.tasks.map((task) => (
{tasks.length ? (
tasks.map((task) => (
<TasksListDataRow
key={task.id}
task={task}
show_scheduled_at={props.show_scheduled_at}
show_scheduled_at={showScheduledAt}
/>
))
) : (
@ -27,7 +27,7 @@ function TasksList(props) {
)}
</tbody>
<tfoot>
<TasksListLabelsRow show_scheduled_at={props.show_scheduled_at} />
<TasksListLabelsRow show_scheduled_at={showScheduledAt} />
</tfoot>
</table>
);
@ -38,8 +38,4 @@ TasksList.propTypes = {
tasks: PropTypes.arrayOf(TasksListDataRow.propTypes.task).isRequired,
};
TasksList.defaultProps = {
show_scheduled_at: false,
};
export { TasksList };

View File

@ -33,14 +33,15 @@ class ListingFilters extends Component {
handleEmptyTrash = () => this.props.onEmptyTrash();
handleFilterAction = () => {
const { onSelectFilter, onBeforeSelectFilter = undefined } = this.props;
const filters = {};
this.getAvailableFilters().forEach((filter, i) => {
filters[this[`filter-${i}`].name] = this[`filter-${i}`].value;
});
if (this.props.onBeforeSelectFilter) {
this.props.onBeforeSelectFilter(filters);
if (onBeforeSelectFilter) {
onBeforeSelectFilter(filters);
}
return this.props.onSelectFilter(filters);
return onSelectFilter(filters);
};
render() {
@ -98,9 +99,6 @@ ListingFilters.propTypes = {
group: PropTypes.string.isRequired,
};
ListingFilters.defaultProps = {
onBeforeSelectFilter: undefined,
};
ListingFilters.displayName = 'ListingFilters';
const ListingFiltersWithBoundary = withBoundary(ListingFilters);
export { ListingFiltersWithBoundary as ListingFilters };

View File

@ -16,15 +16,22 @@ class ListingHeader extends Component {
}
render() {
const columns = this.props.columns.map((column, index) => {
const {
onSort,
sort_by: sortBy,
sort_order: sortOrder,
is_selectable: isSelectable,
selection,
columns: propsColumns = [],
} = this.props;
const columns = propsColumns.map((column, index) => {
const renderColumn = column;
renderColumn.is_primary = index === 0;
renderColumn.sorted =
this.props.sort_by === column.name ? this.props.sort_order : 'desc';
renderColumn.sorted = sortBy === column.name ? sortOrder : 'desc';
return (
<ListingColumn
onSort={this.props.onSort}
sort_by={this.props.sort_by}
onSort={onSort}
sort_by={sortBy}
key={`column-${column.name}`}
column={renderColumn}
/>
@ -33,7 +40,7 @@ class ListingHeader extends Component {
let checkbox;
if (this.props.is_selectable === true) {
if (isSelectable === true) {
checkbox = (
<th className="manage-column column-cb mailpoet-listing-check-column">
<label className="screen-reader-text" htmlFor="select_all">
@ -43,7 +50,7 @@ class ListingHeader extends Component {
name="select_all"
id="select_all"
automationId="select_all"
checked={this.props.selection}
checked={selection}
onCheck={() => {}}
onChange={this.handleSelectItems}
/>
@ -70,10 +77,4 @@ ListingHeader.propTypes = {
selection: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]).isRequired,
};
ListingHeader.defaultProps = {
columns: [],
sort_by: undefined,
sort_order: 'desc',
};
export { ListingHeader };

View File

@ -10,17 +10,18 @@ class ListingColumn extends Component {
};
render() {
const { column, sort_by: sortBy = undefined } = this.props;
const classes = classnames(
'manage-column',
{ 'column-primary': this.props.column.is_primary },
{ sortable: this.props.column.sortable },
this.props.column.sorted,
{ sorted: this.props.sort_by === this.props.column.name },
this.props.column.className,
{ 'column-primary': column.is_primary },
{ sortable: column.sortable },
column.sorted,
{ sorted: sortBy === column.name },
column.className,
);
let label;
if (this.props.column.sortable === true) {
if (column.sortable === true) {
label = (
<a
onClick={this.handleSort}
@ -36,21 +37,21 @@ class ListingColumn extends Component {
}
}}
>
<span>{this.props.column.label}</span>
<span>{column.label}</span>
<span className="mailpoet-listing-sorting-arrow" />
</a>
);
} else {
label = this.props.column.label;
label = column.label;
}
return (
<th
role="columnheader"
className={classes}
id={this.props.column.name}
id={column.name}
scope="col"
width={this.props.column.width || null}
data-automation-id={`listing-column-header-${this.props.column.name}`}
width={column.width || null}
data-automation-id={`listing-column-header-${column.name}`}
>
{label}
</th>
@ -72,8 +73,4 @@ ListingColumn.propTypes = {
onSort: PropTypes.func.isRequired,
};
ListingColumn.defaultProps = {
sort_by: undefined,
};
export { ListingColumn };

View File

@ -27,7 +27,22 @@ class ListingItem extends Component {
render() {
let checkbox = false;
if (this.props.is_selectable === true) {
const {
is_selectable: isSelectable,
item,
columns,
group,
selection,
item_actions: propsItemActions,
onRefreshItems,
isItemInactive,
onRenderItem,
location = undefined,
isItemDeletable = () => true,
isItemToggleable = () => false,
} = this.props;
if (isSelectable === true) {
checkbox = (
<th
className="mailpoet-listing-check-column mailpoet-hide-on-mobile"
@ -35,24 +50,24 @@ class ListingItem extends Component {
>
<label
className="screen-reader-text"
htmlFor={`listing-row-checkbox-${this.props.item.id}`}
htmlFor={`listing-row-checkbox-${item.id}`}
>
{`Select ${this.props.item[this.props.columns[0].name]}`}
{`Select ${item[columns[0].name]}`}
</label>
<Checkbox
value={this.props.item.id}
checked={this.props.item.selected || this.props.selection === 'all'}
value={item.id}
checked={item.selected || selection === 'all'}
onCheck={() => {}}
onChange={this.handleSelectItem}
disabled={this.props.selection === 'all'}
id={`listing-row-checkbox-${this.props.item.id}`}
automationId={`listing-row-checkbox-${this.props.item.id}`}
disabled={selection === 'all'}
id={`listing-row-checkbox-${item.id}`}
automationId={`listing-row-checkbox-${item.id}`}
/>
</th>
);
}
const customActions = this.props.item_actions;
const customActions = propsItemActions;
let itemActions = false;
if (customActions.length > 0) {
@ -75,10 +90,10 @@ class ListingItem extends Component {
href="#"
onClick={(event) => {
event.preventDefault();
this.handleTrashItem(this.props.item.id);
this.handleTrashItem(item.id);
}}
>
{this.props.isItemToggleable(this.props.item)
{isItemToggleable(item)
? __('Trash and disable', 'mailpoet')
: __('Move to trash', 'mailpoet')}
</a>
@ -87,7 +102,7 @@ class ListingItem extends Component {
} else if (action.refresh) {
customAction = (
<span
onClick={this.props.onRefreshItems}
onClick={onRefreshItems}
key={`action-${action.name}`}
className={classnames(action.name, action.className)}
role="button"
@ -98,11 +113,11 @@ class ListingItem extends Component {
['Enter', ' '].includes(event.key)
) {
event.preventDefault();
this.props.onRefreshItems();
onRefreshItems();
}
}}
>
{action.link(this.props.item)}
{action.link(item)}
</span>
);
} else if (action.link) {
@ -111,7 +126,7 @@ class ListingItem extends Component {
key={`action-${action.name}`}
className={classnames(action.name, action.className)}
>
{action.link(this.props.item, this.props.location)}
{action.link(item, location)}
</span>
);
} else {
@ -125,10 +140,7 @@ class ListingItem extends Component {
onClick={(event) => {
event.preventDefault();
if (action.onClick !== undefined) {
action.onClick(
this.props.item,
this.props.onRefreshItems,
);
action.onClick(item, onRefreshItems);
}
}}
>
@ -144,9 +156,9 @@ class ListingItem extends Component {
<span className="edit mailpoet-hide-on-mobile">
<Link
to={{
pathname: `/edit/${this.props.item.id}`,
pathname: `/edit/${item.id}`,
state: {
backUrl: this.props.location?.pathname,
backUrl: location?.pathname,
},
}}
>
@ -158,7 +170,7 @@ class ListingItem extends Component {
let actions;
if (this.props.group === 'trash') {
if (group === 'trash') {
actions = (
<div className="mailpoet-listing-actions-holder">
<div className="mailpoet-listing-actions">
@ -167,22 +179,22 @@ class ListingItem extends Component {
href="#"
onClick={(event) => {
event.preventDefault();
this.handleRestoreItem(this.props.item.id);
this.handleRestoreItem(item.id);
}}
>
{this.props.isItemToggleable(this.props.item)
{isItemToggleable(item)
? __('Restore and enable', 'mailpoet')
: __('Restore', 'mailpoet')}
</a>
</span>
{this.props.isItemDeletable(this.props.item) && (
{isItemDeletable(item) && (
<span className="delete">
<a
className="submitdelete"
href="#"
onClick={(event) => {
event.preventDefault();
this.handleDeleteItem(this.props.item.id);
this.handleDeleteItem(item.id);
}}
>
{__('Delete permanently', 'mailpoet')}
@ -201,20 +213,14 @@ class ListingItem extends Component {
}
const rowClasses = classnames({
'mailpoet-listing-row-selected':
this.props.item.selected || this.props.selection === 'all',
'mailpoet-listing-row-inactive': this.props.isItemInactive(
this.props.item,
),
'mailpoet-listing-row-selected': item.selected || selection === 'all',
'mailpoet-listing-row-inactive': isItemInactive(item),
});
return (
<tr
className={rowClasses}
data-automation-id={`listing_item_${this.props.item.id}`}
>
<tr className={rowClasses} data-automation-id={`listing_item_${item.id}`}>
{checkbox}
{this.props.onRenderItem(this.props.item, actions)}
{onRenderItem(item, actions)}
</tr>
);
}
@ -245,10 +251,4 @@ ListingItem.propTypes = {
isItemToggleable: PropTypes.func,
};
ListingItem.defaultProps = {
location: undefined,
isItemDeletable: () => true,
isItemToggleable: () => false,
};
export { ListingItem };

View File

@ -10,20 +10,43 @@ import { ListingItem } from 'listing/listing-item.jsx';
// eslint-disable-next-line react/prefer-stateless-function, max-len
class ListingItems extends Component {
render() {
if (this.props.items.length === 0) {
const {
bulk_actions: bulkActions,
count,
columns,
group,
items,
is_selectable: isSelectable,
isItemInactive,
item_actions: itemActions,
limit,
loading,
messages,
onBulkAction,
onDeleteItem,
onRefreshItems,
onRenderItem,
onRestoreItem,
onSelectAll,
onSelectItem,
onTrashItem,
selected_ids: selectedIds,
selection,
getListingItemKey = undefined,
search = undefined,
location = undefined,
isItemDeletable = () => true,
isItemToggleable = () => false,
} = this.props;
if (items.length === 0) {
let message;
if (this.props.loading === true) {
if (loading === true) {
message =
(this.props.messages.onLoadingItems &&
this.props.messages.onLoadingItems(this.props.group)) ||
(messages.onLoadingItems && messages.onLoadingItems(group)) ||
__('Loading ...', 'mailpoet');
} else {
message =
(this.props.messages.onNoItemsFound &&
this.props.messages.onNoItemsFound(
this.props.group,
this.props.search,
)) ||
(messages.onNoItemsFound && messages.onNoItemsFound(group, search)) ||
__('No items found.', 'mailpoet');
}
@ -31,9 +54,7 @@ class ListingItems extends Component {
<tbody>
<tr className="mailpoet-listing-no-items">
<td
colSpan={
this.props.columns.length + (this.props.is_selectable ? 1 : 0)
}
colSpan={columns.length + (isSelectable ? 1 : 0)}
className="colspanchange"
>
{message}
@ -43,11 +64,8 @@ class ListingItems extends Component {
);
}
const isSelectAllHidden =
this.props.selection === false || this.props.count <= this.props.limit;
const areBulkActionsHidden = !(
this.props.selected_ids.length > 0 || this.props.selection
);
const isSelectAllHidden = selection === false || count <= limit;
const areBulkActionsHidden = !(selectedIds.length > 0 || selection);
const actionAndSelectAllRowClasses = classnames(
'mailpoet-listing-actions-and-select-all-row',
@ -62,39 +80,35 @@ class ListingItems extends Component {
return (
<tbody>
<tr className={actionAndSelectAllRowClasses}>
<td
colSpan={
this.props.columns.length + (this.props.is_selectable ? 1 : 0)
}
>
<td colSpan={columns.length + (isSelectable ? 1 : 0)}>
<Grid.SpaceBetween verticalAlign="center">
<div className="mailpoet-listing-bulk-actions-container">
{!areBulkActionsHidden && (
<ListingBulkActions
count={this.props.count}
bulk_actions={this.props.bulk_actions}
selection={this.props.selection}
selected_ids={this.props.selected_ids}
onBulkAction={this.props.onBulkAction}
count={count}
bulk_actions={bulkActions}
selection={selection}
selected_ids={selectedIds}
onBulkAction={onBulkAction}
/>
)}
</div>
<div className={selectAllClasses}>
{this.props.selection !== 'all'
{selection !== 'all'
? __('All items on this page are selected.', 'mailpoet')
: __('All %d items are selected.', 'mailpoet').replace(
'%d',
this.props.count.toLocaleString(),
count.toLocaleString(),
)}
&nbsp;
<a
href="#"
onClick={(event) => {
event.preventDefault();
this.props.onSelectAll(event);
onSelectAll(event);
}}
>
{this.props.selection !== 'all'
{selection !== 'all'
? __('Select all items on all pages', 'mailpoet')
: __('Clear selection', 'mailpoet')}
</a>
@ -104,35 +118,34 @@ class ListingItems extends Component {
</td>
</tr>
{this.props.items.map((item) => {
{items.map((item) => {
const renderItem = item;
renderItem.id = parseInt(item.id, 10);
renderItem.selected =
this.props.selected_ids.indexOf(renderItem.id) !== -1;
renderItem.selected = selectedIds.indexOf(renderItem.id) !== -1;
let key = `item-${renderItem.id}-${item.id}`;
if (typeof this.props.getListingItemKey === 'function') {
key = this.props.getListingItemKey(item);
if (typeof getListingItemKey === 'function') {
key = getListingItemKey(item);
}
return (
<ListingItem
columns={this.props.columns}
isItemInactive={this.props.isItemInactive}
onSelectItem={this.props.onSelectItem}
onRenderItem={this.props.onRenderItem}
onDeleteItem={this.props.onDeleteItem}
onRestoreItem={this.props.onRestoreItem}
onTrashItem={this.props.onTrashItem}
onRefreshItems={this.props.onRefreshItems}
selection={this.props.selection}
is_selectable={this.props.is_selectable}
item_actions={this.props.item_actions}
group={this.props.group}
location={this.props.location}
columns={columns}
isItemInactive={isItemInactive}
onSelectItem={onSelectItem}
onRenderItem={onRenderItem}
onDeleteItem={onDeleteItem}
onRestoreItem={onRestoreItem}
onTrashItem={onTrashItem}
onRefreshItems={onRefreshItems}
selection={selection}
is_selectable={isSelectable}
item_actions={itemActions}
group={group}
location={location}
key={key}
item={renderItem}
isItemDeletable={this.props.isItemDeletable}
isItemToggleable={this.props.isItemToggleable}
isItemDeletable={isItemDeletable}
isItemToggleable={isItemToggleable}
/>
);
})}
@ -179,12 +192,4 @@ ListingItems.propTypes = {
isItemToggleable: PropTypes.func,
};
ListingItems.defaultProps = {
getListingItemKey: undefined,
search: undefined,
location: undefined,
isItemDeletable: () => true,
isItemToggleable: () => false,
};
export { ListingItems };

View File

@ -60,7 +60,8 @@ class ListingPages extends Component {
Math.min(Math.max(1, Math.abs(Number(page))), this.getLastPage());
render() {
if (this.props.count === 0) {
const { count, limit, page, position = '' } = this.props;
if (count === 0) {
return false;
}
let pagination = false;
@ -87,8 +88,8 @@ class ListingPages extends Component {
</span>
);
if (this.props.limit > 0 && this.props.count > this.props.limit) {
if (this.props.page > 1) {
if (limit > 0 && count > limit) {
if (page > 1) {
previousPage = (
<a
href="#"
@ -108,7 +109,7 @@ class ListingPages extends Component {
);
}
if (this.props.page > 2) {
if (page > 2) {
firstPage = (
<a
href="#"
@ -129,7 +130,7 @@ class ListingPages extends Component {
);
}
if (this.props.page < this.getLastPage()) {
if (page < this.getLastPage()) {
nextPage = (
<a
href="#"
@ -149,7 +150,7 @@ class ListingPages extends Component {
);
}
if (this.props.page < this.getLastPage() - 1) {
if (page < this.getLastPage() - 1) {
lastPage = (
<a
href="#"
@ -170,7 +171,7 @@ class ListingPages extends Component {
);
}
let pageValue = this.props.page;
let pageValue = page;
if (this.state.page !== null) {
pageValue = this.state.page;
}
@ -184,7 +185,7 @@ class ListingPages extends Component {
<span className="mailpoet-listing-paging-input">
<label
className="screen-reader-text"
htmlFor={`current-page-selector-${this.props.position}`}
htmlFor={`current-page-selector-${position}`}
>
{__('Current page', 'mailpoet')}
</label>
@ -197,13 +198,13 @@ class ListingPages extends Component {
size="2"
value={pageValue}
name="paged"
id={`current-page-selector-${this.props.position}`}
id={`current-page-selector-${position}`}
className="mailpoet-listing-current-page"
/>
{__('of', 'mailpoet')}
&nbsp;
<span className="mailpoet-listing-total-pages">
{Math.ceil(this.props.count / this.props.limit).toLocaleString()}
{Math.ceil(count / limit).toLocaleString()}
</span>
</span>
&nbsp;
@ -215,16 +216,16 @@ class ListingPages extends Component {
}
const classes = classnames('mailpoet-listing-pages', {
'one-page': this.props.count <= this.props.limit,
'one-page': count <= limit,
});
let numberOfItemsLabel;
if (Number(this.props.count) === 1) {
if (Number(count) === 1) {
numberOfItemsLabel = __('1 item', 'mailpoet');
} else {
numberOfItemsLabel = __('%1$d items', 'mailpoet').replace(
'%1$d',
parseInt(this.props.count, 10).toLocaleString(),
parseInt(count, 10).toLocaleString(),
);
}
@ -245,16 +246,12 @@ ListingPages.propTypes = {
limit: PropTypes.number.isRequired,
};
ListingPages.defaultProps = {
position: '',
};
/* type ArrowProps = {
direction?: 'right',
disabled?: boolean
} */
function Arrow({ direction, disabled }) {
function Arrow({ direction = 'right', disabled = false }) {
const arrowLeftPath =
'M8 10V2c0-.552-.448-1-1-1-.216 0-.427.07-.6.2l-5.333 4c-.442.331-.532.958-.2 1.4.057.076.124.143.2.2l5.333 4c.442.331 1.069.242 1.4-.2.13-.173.2-.384.2-.6z';
const arrowRightPath =
@ -279,9 +276,4 @@ Arrow.propTypes = {
disabled: PropTypes.bool,
};
Arrow.defaultProps = {
direction: 'right',
disabled: false,
};
export { ListingPages };

View File

@ -95,7 +95,10 @@ class EventScheduling extends Component {
}
displayAfterTimeNumberField() {
const { afterTimeNumberSize, event } = this.props;
const {
event,
afterTimeNumberSize = defaultAfterTimeNumberInputFieldSize,
} = this.props;
const { afterTimeType, afterTimeNumber } = this.state;
if (afterTimeType === 'immediate') return null;
if (
@ -135,7 +138,7 @@ class EventScheduling extends Component {
}
propagateChange(data) {
const { onValueChange } = this.props;
const { onValueChange = null } = this.props;
if (!onValueChange) return;
onValueChange(data);
@ -197,9 +200,5 @@ EventScheduling.propTypes = {
}).isRequired,
};
EventScheduling.defaultProps = {
afterTimeNumberSize: defaultAfterTimeNumberInputFieldSize,
onValueChange: null,
};
EventScheduling.displayName = 'EventScheduling';
export { EventScheduling };

View File

@ -149,7 +149,7 @@ class SendEventConditions extends Component {
segment,
eventOptionValue,
} = this.state;
const { onValueChange } = this.props;
const { onValueChange = null } = this.props;
if (!onValueChange) return;
const options = {
@ -193,8 +193,5 @@ SendEventConditions.propTypes = {
onValueChange: PropTypes.func,
};
SendEventConditions.defaultProps = {
onValueChange: null,
};
SendEventConditions.displayName = 'SendEventConditions';
export { SendEventConditions };

View File

@ -30,7 +30,11 @@ const wrapInLink = (content, params, id, totalSent) => {
);
};
function Statistics({ newsletter, isSent, currentTime }) {
function Statistics({
newsletter,
isSent = undefined,
currentTime = undefined,
}) {
let sent = isSent;
if (sent === undefined) {
// condition for standard and post notification listings
@ -199,10 +203,6 @@ Statistics.propTypes = {
currentTime: PropTypes.string,
};
Statistics.defaultProps = {
isSent: undefined,
currentTime: undefined,
};
Statistics.displayName = 'NewsletterStatistics';
const StatisticsWithBoundary = withBoundary(Statistics);
export { StatisticsWithBoundary as Statistics };

View File

@ -39,11 +39,12 @@ class SenderField extends Component {
}
onChange(event) {
const { onValueChange = () => {} } = this.props.onValueChange;
const emailAddress = event.target.value.toLowerCase();
this.setState({
emailAddress,
});
this.props.onValueChange({
onValueChange({
...event,
target: {
...event.target,
@ -178,10 +179,5 @@ SenderField.propTypes = {
onValueChange: PropTypes.func,
};
SenderField.defaultProps = {
onValueChange: function onValueChange() {
// no-op
},
};
SenderField.displayName = 'SenderField';
export { SenderField };

View File

@ -5,20 +5,28 @@ import { Select } from 'common/form/select/select.tsx';
// eslint-disable-next-line react/prefer-stateless-function
class TimeSelect extends Component {
render() {
const options = Object.keys(this.props.timeOfDayItems).map((value) => (
<option key={`option-${this.props.timeOfDayItems[value]}`} value={value}>
{this.props.timeOfDayItems[value]}
const {
onChange,
timeOfDayItems,
value,
disabled = false,
name = 'time',
validation = {},
} = this.props;
const options = Object.keys(timeOfDayItems).map((val) => (
<option key={`option-${timeOfDayItems[value]}`} value={val}>
{timeOfDayItems[val]}
</option>
));
return (
<Select
name={this.props.name || 'time'}
value={this.props.value}
disabled={this.props.disabled}
onChange={this.props.onChange}
name={name || 'time'}
value={value}
disabled={disabled}
onChange={onChange}
isMinWidth
{...this.props.validation}
{...validation}
>
{options}
</Select>
@ -35,10 +43,5 @@ TimeSelect.propTypes = {
validation: PropTypes.object, // eslint-disable-line react/forbid-prop-types
};
TimeSelect.defaultProps = {
name: 'time',
disabled: false,
validation: {},
};
TimeSelect.displayName = 'TimeSelect';
export { TimeSelect };

View File

@ -134,7 +134,13 @@ SendingStatusListing.propTypes = {
}).isRequired,
};
function StatsLink({ newsletter }) {
function StatsLink({
newsletter = {
id: null,
subject: null,
sent: false,
},
}) {
if (!newsletter.id || !newsletter.subject || !newsletter.sent) return null;
return (
<p>
@ -150,16 +156,8 @@ StatsLink.propTypes = {
sent: PropTypes.bool,
}),
};
StatsLink.defaultProps = {
newsletter: {
id: null,
subject: null,
sent: false,
},
};
function ListingItem({
error,
failed,
taskId,
processed,
@ -167,6 +165,7 @@ function ListingItem({
subscriberId,
lastName,
firstName,
error = '',
}) {
const resend = () => {
MailPoet.Ajax.post({
@ -263,9 +262,6 @@ ListingItem.propTypes = {
processed: PropTypes.number.isRequired,
subscriberId: PropTypes.number.isRequired,
};
ListingItem.defaultProps = {
error: '',
};
ListingItem.displayName = 'ListingItem';
SendingStatus.displayName = 'SendingStatus';
export { SendingStatus };

View File

@ -91,7 +91,7 @@ class TemplateBox extends Component {
}
render() {
const { index, name, thumbnail, readonly } = this.props;
const { index, name, readonly, thumbnail = null } = this.props;
let preview = '';
if (typeof thumbnail === 'string' && thumbnail.length > 0) {
@ -151,8 +151,5 @@ TemplateBox.propTypes = {
afterSelect: PropTypes.func.isRequired,
};
TemplateBox.defaultProps = {
thumbnail: null,
};
TemplateBox.displayName = 'TemplateBox';
export { TemplateBox };

View File

@ -29,8 +29,8 @@ const MenuItem = WpMenuItem as React.FC<
>;
function NewsletterTypesComponent({
filter,
history,
filter = null,
hideScreenOptions = true,
}: Props): JSX.Element {
const [isCreating, setIsCreating] = useState(false);
@ -291,11 +291,6 @@ function NewsletterTypesComponent({
);
}
NewsletterTypesComponent.defaultProps = {
filter: null,
hideScreenOptions: true,
};
export const NewsletterTypes = withRouter(
NewsletterTypesComponent as ComponentType<RouteComponentProps>,
);

View File

@ -1,7 +1,7 @@
import PropTypes from 'prop-types';
import { Notice } from 'notices/notice.tsx';
function MailerStatusNotice({ error }) {
function MailerStatusNotice({ error = null }) {
if (!error || error.operation !== 'authorization') return null;
return (
<Notice type="error" timeout={false} closable={false}>
@ -15,8 +15,5 @@ MailerStatusNotice.propTypes = {
error_message: PropTypes.string,
}),
};
MailerStatusNotice.defaultProps = {
error: null,
};
export { MailerStatusNotice };

View File

@ -22,14 +22,14 @@ type Props = {
};
function Notice({
onClose,
onDisplay,
renderInPlace,
timeout,
scroll,
children,
closable,
type,
closable = true,
onClose = undefined,
onDisplay = undefined,
renderInPlace = false,
timeout = 10000,
scroll = false,
}: Props) {
const [hidden, setHidden] = useState(false);
const elementRef = useRef(null);
@ -88,14 +88,6 @@ function Notice({
);
}
Notice.defaultProps = {
timeout: 10000,
scroll: false,
closable: true,
renderInPlace: false,
onDisplay: undefined,
onClose: undefined,
};
Notice.displayName = 'Notice';
const NoticeWithBoundary = withBoundary(Notice);
export { NoticeWithBoundary as Notice };

View File

@ -10,7 +10,7 @@ type Props = {
onRequestClose?: () => void;
};
function App({ onRequestClose }: Props) {
function App({ onRequestClose = noop }: Props) {
const [showModal, setShowModal] = useState(false);
// use jQuery since some of the targeted notices are added to the DOM using the old
@ -36,10 +36,6 @@ function App({ onRequestClose }: Props) {
);
}
App.defaultProps = {
onRequestClose: noop,
};
// nothing is actually rendered to the container because the <Modal> component uses
// ReactDOM.createPortal() but we need an element as a React root on all pages
const container = document.getElementById('mailpoet_set_from_address_modal');

View File

@ -3,11 +3,11 @@ import { MailPoet } from 'mailpoet';
import { Button } from 'common/button/button';
function PreviousNextStepButtons({
hidePrevious,
isLastStep,
canGoNext,
onPreviousAction,
onNextAction,
hidePrevious = false,
isLastStep = false,
canGoNext = true,
onPreviousAction = () => {},
onNextAction = () => {},
}) {
return (
<div className="mailpoet-settings-save">
@ -40,12 +40,5 @@ PreviousNextStepButtons.propTypes = {
onNextAction: PropTypes.func,
};
PreviousNextStepButtons.defaultProps = {
hidePrevious: false,
isLastStep: false,
canGoNext: true,
onPreviousAction: () => {},
onNextAction: () => {},
};
PreviousNextStepButtons.displayName = 'PreviousNextStepButtons';
export { PreviousNextStepButtons };

View File

@ -26,9 +26,9 @@ function getPreviousStepLink(importData, subscribersLimitForValidation) {
function StepDataManipulationComponent({
history,
stepMethodSelectionData,
subscribersLimitForValidation,
setStepDataManipulationData,
stepMethodSelectionData = undefined,
}) {
const [selectedSegments, setSelectedSegments] = useState([]);
const [updateExistingSubscribers, setUpdateExistingSubscribers] =
@ -132,8 +132,4 @@ StepDataManipulationComponent.propTypes = {
setStepDataManipulationData: PropTypes.func.isRequired,
};
StepDataManipulationComponent.defaultProps = {
stepMethodSelectionData: undefined,
};
export const StepDataManipulation = withRouter(StepDataManipulationComponent);

View File

@ -7,7 +7,7 @@ import { matchColumns } from './match-columns.jsx';
const MAX_SUBSCRIBERS_SHOWN = 10;
function ColumnDataMatch({ header, subscribers }) {
function ColumnDataMatch({ header = [], subscribers }) {
const matchedColumnTypes = matchColumns(subscribers, header);
return (
<tr>
@ -43,10 +43,6 @@ ColumnDataMatch.propTypes = {
header: PropTypes.arrayOf(PropTypes.string),
};
ColumnDataMatch.defaultProps = {
header: [],
};
function Header({ header }) {
return (
<tr className="mailpoet_header">
@ -127,7 +123,7 @@ Subscribers.propTypes = {
).isRequired,
};
function MatchTable({ subscribersCount, subscribers, header }) {
function MatchTable({ subscribersCount = 0, subscribers = [], header = [] }) {
useLayoutEffect(() => {
generateColumnSelection();
});
@ -165,10 +161,4 @@ MatchTable.propTypes = {
header: PropTypes.arrayOf(PropTypes.string),
};
MatchTable.defaultProps = {
subscribersCount: 0,
subscribers: [],
header: [],
};
export { MatchTable };

View File

@ -24,7 +24,13 @@ SingleWarning.propTypes = {
subscribers: PropTypes.arrayOf(PropTypes.string).isRequired,
};
function Warnings({ stepMethodSelectionData }) {
function Warnings({
stepMethodSelectionData = {
invalid: [],
duplicate: [],
role: [],
},
}) {
const { invalid, duplicate, role } = stepMethodSelectionData;
const [detailsShown, setDetailsShown] = useState(false);
@ -125,12 +131,4 @@ Warnings.propTypes = {
}),
};
Warnings.defaultProps = {
stepMethodSelectionData: {
invalid: [],
duplicate: [],
role: [],
},
};
export { Warnings };

View File

@ -22,8 +22,8 @@ type Props = {
};
function StepInputValidationComponent({
stepMethodSelectionData,
history,
stepMethodSelectionData = undefined,
}: Props): JSX.Element {
const [importSource, setImportSource] = useState(undefined);
const [lastSent, setLastSent] = useState(undefined);
@ -69,10 +69,6 @@ function StepInputValidationComponent({
);
}
StepInputValidationComponent.defaultProps = {
stepMethodSelectionData: undefined,
};
StepInputValidationComponent.displayName = 'StepInputValidationComponent';
export const StepInputValidation = withRouter(

View File

@ -7,7 +7,7 @@ import { Selection } from 'form/fields/selection.jsx';
import ReactStringReplace from 'react-string-replace';
import { PreviousNextStepButtons } from '../previous-next-step-buttons.jsx';
function MethodMailChimp({ onFinish, onPrevious }) {
function MethodMailChimp({ onFinish = () => {}, onPrevious = () => {} }) {
const [key, setKey] = useState('');
const [mailChimpLoadedLists, setMailChimpLoadedLists] = useState(undefined);
const [selectedLists, setSelectedLists] = useState([]);
@ -148,11 +148,6 @@ MethodMailChimp.propTypes = {
onPrevious: PropTypes.func,
};
MethodMailChimp.defaultProps = {
onFinish: () => {},
onPrevious: () => {},
};
MethodMailChimp.displayName = 'MethodMailChimp';
export { MethodMailChimp };

View File

@ -10,7 +10,13 @@ const kbLink =
const placeholder =
'Email, First Name, Last Name\njohn@doe.com, John, Doe\nmary@smith.com, Mary, Smith\njohnny@walker.com, Johnny, Walker';
function MethodPaste({ onValueChange, canFinish, onFinish, data, onPrevious }) {
function MethodPaste({
canFinish,
onValueChange,
data = '',
onFinish = () => {},
onPrevious = () => {},
}) {
const onChange = (e) => {
onValueChange(e.target.value);
};
@ -64,10 +70,5 @@ MethodPaste.propTypes = {
data: PropTypes.string,
};
MethodPaste.defaultProps = {
onFinish: () => {},
onPrevious: () => {},
data: '',
};
MethodPaste.displayName = 'MethodPaste';
export { MethodPaste };

View File

@ -7,7 +7,12 @@ import { PreviousNextStepButtons } from '../previous-next-step-buttons.jsx';
const kbLink =
'https://kb.mailpoet.com/article/126-importing-subscribers-with-csv-files';
function MethodUpload({ onValueChange, canFinish, onFinish, onPrevious }) {
function MethodUpload({
onValueChange,
canFinish,
onFinish = () => {},
onPrevious = () => {},
}) {
const onChange = (e) => {
const ext = e.target.value.match(/[^.]+$/);
MailPoet.Notice.hide();
@ -66,9 +71,5 @@ MethodUpload.propTypes = {
onValueChange: PropTypes.func.isRequired,
};
MethodUpload.defaultProps = {
onFinish: () => {},
onPrevious: () => {},
};
MethodUpload.displayName = 'MethodUpload';
export { MethodUpload };

View File

@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import { Radio } from 'common/form/radio/radio';
import { Tag } from 'common/tag/tag';
function SelectImportMethod({ activeMethod, onMethodChange }) {
function SelectImportMethod({ onMethodChange, activeMethod = undefined }) {
return (
<>
<div className="mailpoet-settings-label">
@ -74,8 +74,5 @@ SelectImportMethod.propTypes = {
onMethodChange: PropTypes.func.isRequired,
};
SelectImportMethod.defaultProps = {
activeMethod: undefined,
};
SelectImportMethod.displayName = 'SelectImportMethod';
export { SelectImportMethod };

View File

@ -8,7 +8,11 @@ import ReactStringReplace from 'react-string-replace';
import { Button } from 'common/button/button';
import { ErrorBoundary } from 'common';
function ResultMessage({ subscribersCount, segments, initialMessage }) {
function ResultMessage({
subscribersCount = 0,
segments = [],
initialMessage = '',
}) {
if (subscribersCount) {
let message = ReactStringReplace(initialMessage, '%1$s', () => (
<strong key="%1$s">{subscribersCount.toLocaleString()}</strong>
@ -29,14 +33,9 @@ ResultMessage.propTypes = {
initialMessage: PropTypes.string,
};
ResultMessage.defaultProps = {
segments: [],
subscribersCount: 0,
initialMessage: '',
};
ResultMessage.displayName = 'ResultMessage';
function NoAction({ createdSubscribers, updatedSubscribers }) {
function NoAction({ createdSubscribers = 0, updatedSubscribers = 0 }) {
if (!createdSubscribers && !updatedSubscribers) {
return <p>{MailPoet.I18n.t('importNoAction')}</p>;
}
@ -48,13 +47,12 @@ NoAction.propTypes = {
updatedSubscribers: PropTypes.number,
};
NoAction.defaultProps = {
createdSubscribers: 0,
updatedSubscribers: 0,
};
NoAction.displayName = 'NoAction';
function SuppressionListReminder({ createdSubscribers, updatedSubscribers }) {
function SuppressionListReminder({
createdSubscribers = 0,
updatedSubscribers = 0,
}) {
if (createdSubscribers || updatedSubscribers) {
return (
<>
@ -89,13 +87,9 @@ SuppressionListReminder.propTypes = {
updatedSubscribers: PropTypes.number,
};
SuppressionListReminder.defaultProps = {
createdSubscribers: 0,
updatedSubscribers: 0,
};
SuppressionListReminder.displayName = 'SuppressionListReminder';
function NoWelcomeEmail({ addedToSegmentWithWelcomeNotification }) {
function NoWelcomeEmail({ addedToSegmentWithWelcomeNotification = false }) {
if (addedToSegmentWithWelcomeNotification) {
return <p>{MailPoet.I18n.t('importNoWelcomeEmail')}</p>;
}
@ -106,18 +100,15 @@ NoWelcomeEmail.propTypes = {
addedToSegmentWithWelcomeNotification: PropTypes.bool,
};
NoWelcomeEmail.defaultProps = {
addedToSegmentWithWelcomeNotification: false,
};
NoWelcomeEmail.diplayName = 'NoWelcomeEmail';
function StepResultsComponent({
errors,
createdSubscribers,
updatedSubscribers,
segments,
addedToSegmentWithWelcomeNotification,
history,
errors = [],
createdSubscribers = undefined,
updatedSubscribers = undefined,
segments = undefined,
addedToSegmentWithWelcomeNotification = undefined,
}) {
useEffect(() => {
if (
@ -204,12 +195,5 @@ StepResultsComponent.propTypes = {
addedToSegmentWithWelcomeNotification: PropTypes.bool,
};
StepResultsComponent.defaultProps = {
errors: [],
segments: undefined,
createdSubscribers: undefined,
updatedSubscribers: undefined,
addedToSegmentWithWelcomeNotification: undefined,
};
StepResultsComponent.displayName = 'StepResultsComponent';
export const StepResults = withRouter(StepResultsComponent);

View File

@ -38,6 +38,7 @@ module.exports = [
'no-underscore-dangle': 0, // Backbone uses underscores, we cannot remove them
'import/no-default-export': 1, // no default exports
'no-only-tests/no-only-tests': 2,
'react/require-default-props': 0, // deprecated in react 18.3.1
'check-file/filename-naming-convention': [
'error',
{ '**/*.*': 'KEBAB_CASE' },

View File

@ -47,6 +47,7 @@ module.exports = [
'no-only-tests/no-only-tests': 2,
'class-methods-use-this': 0,
'react/jsx-props-no-spreading': 0,
'react/require-default-props': 0, // deprecated in react 18.3.1
'import/extensions': 0, // we wouldn't be able to import jQuery without this line
'import/prefer-default-export': 0, // we want to stop using default exports and start using named exports
'import/no-default-export': 1, // no default exports