From 6bac8fc2665da74929e3fe945351bed2f3d02b44 Mon Sep 17 00:00:00 2001 From: Yashwin Date: Wed, 14 Sep 2022 14:51:19 +0530 Subject: [PATCH 1/2] Update style for plugin action confirmation modal popups --- client/lib/accept/dialog.jsx | 1 + .../plugins/plugin-remove-button/index.jsx | 32 ++- .../my-sites/plugins/plugins-list/index.jsx | 254 +++++++----------- client/my-sites/plugins/style.scss | 29 +- client/my-sites/plugins/utils.js | 89 +++++- 5 files changed, 223 insertions(+), 182 deletions(-) diff --git a/client/lib/accept/dialog.jsx b/client/lib/accept/dialog.jsx index 836e51575a5f2..b114139d7e04c 100644 --- a/client/lib/accept/dialog.jsx +++ b/client/lib/accept/dialog.jsx @@ -59,6 +59,7 @@ class AcceptDialog extends Component { onClose={ this.onClose } className="accept__dialog" isVisible + additionalClassNames={ this.props?.options?.additionalClassNames } > { this.props.message } diff --git a/client/my-sites/plugins/plugin-remove-button/index.jsx b/client/my-sites/plugins/plugin-remove-button/index.jsx index c9928e53396b8..782642c238a4c 100644 --- a/client/my-sites/plugins/plugin-remove-button/index.jsx +++ b/client/my-sites/plugins/plugin-remove-button/index.jsx @@ -19,6 +19,7 @@ import PluginAction from 'calypso/my-sites/plugins/plugin-action/plugin-action'; import { removePlugin } from 'calypso/state/plugins/installed/actions'; import { isPluginActionInProgress } from 'calypso/state/plugins/installed/selectors'; import { removePluginStatuses } from 'calypso/state/plugins/installed/status/actions'; +import { getPluginActionDailogMessage } from '../utils'; import './style.scss'; @@ -26,25 +27,22 @@ class PluginRemoveButton extends Component { static displayName = 'PluginRemoveButton'; removeAction = () => { + const { translate, plugin, site } = this.props; + const dialogOptions = { + additionalClassNames: 'plugins__confirmation-modal', + isScary: true, + }; + const heading = translate( 'Remove %(pluginName)s', { + args: { + pluginName: plugin.name, + }, + } ); accept( - this.props.translate( - 'Are you sure you want to remove {{strong}}%(pluginName)s{{/strong}} from' + - ' %(siteName)s? {{br /}} {{em}}This will deactivate the plugin and delete all' + - ' associated files and data.{{/em}}', - { - components: { - em: , - br:
, - strong: , - }, - args: { - pluginName: this.props.plugin.name, - siteName: this.props.site.title, - }, - } - ), + getPluginActionDailogMessage( [ site ], [ plugin ], heading, 'deactivate and delete' ), this.processRemovalConfirmation, - this.props.translate( 'Remove' ) + heading, + null, + dialogOptions ); }; diff --git a/client/my-sites/plugins/plugins-list/index.jsx b/client/my-sites/plugins/plugins-list/index.jsx index 7d3c03f57d1a9..ad95bc1df7bf2 100644 --- a/client/my-sites/plugins/plugins-list/index.jsx +++ b/client/my-sites/plugins/plugins-list/index.jsx @@ -33,6 +33,7 @@ import siteHasFeature from 'calypso/state/selectors/site-has-feature'; import { isJetpackSite } from 'calypso/state/sites/selectors'; import { getSelectedSite, getSelectedSiteSlug } from 'calypso/state/ui/selectors'; import PluginManagementV2 from '../plugin-management-v2'; +import { getPluginActionDailogMessage } from '../utils'; import './style.scss'; @@ -347,102 +348,8 @@ export class PluginsList extends Component { } }; - getConfirmationText( selectedPlugins, actionText, actionPreposition ) { - const pluginsList = {}; - const sitesList = {}; - let pluginName; - let siteName; - const { translate } = this.props; - - selectedPlugins.forEach( ( plugin ) => { - pluginsList[ plugin.slug ] = true; - pluginName = plugin.name || plugin.slug; - - Object.keys( plugin.sites ).forEach( ( siteId ) => { - const site = this.props.allSites.find( ( s ) => s.ID === parseInt( siteId ) ); - if ( site.canUpdateFiles ) { - sitesList[ site.ID ] = true; - siteName = site.title; - } - } ); - } ); - - const pluginsListSize = Object.keys( pluginsList ).length; - const siteListSize = Object.keys( sitesList ).length; - const combination = - ( siteListSize > 1 ? 'n sites' : '1 site' ) + - ' ' + - ( pluginsListSize > 1 ? 'n plugins' : '1 plugin' ); - - switch ( combination ) { - case '1 site 1 plugin': - return translate( - 'You are about to %(actionText)s {{em}}%(plugin)s %(actionPreposition)s %(site)s{{/em}}.', - { - components: { - em: , - }, - args: { - actionText: actionText, - actionPreposition: actionPreposition, - plugin: pluginName, - site: siteName, - }, - } - ); - - case '1 site n plugins': - return translate( - 'You are about to %(actionText)s {{em}}%(numberOfPlugins)d plugins %(actionPreposition)s %(site)s{{/em}}.', - { - components: { - em: , - }, - args: { - actionText: actionText, - actionPreposition: actionPreposition, - numberOfPlugins: pluginsListSize, - site: siteName, - }, - } - ); - - case 'n sites 1 plugin': - return translate( - 'You are about to %(actionText)s {{em}}%(plugin)s %(actionPreposition)s %(numberOfSites)d sites{{/em}}.', - { - components: { - em: , - }, - args: { - actionText: actionText, - actionPreposition: actionPreposition, - plugin: pluginName, - numberOfSites: siteListSize, - }, - } - ); - - case 'n sites n plugins': - return translate( - 'You are about to %(actionText)s {{em}}%(numberOfPlugins)d plugins %(actionPreposition)s %(numberOfSites)d sites{{/em}}.', - { - components: { - em: , - }, - args: { - actionText: actionText, - actionPreposition: actionPreposition, - numberOfPlugins: pluginsListSize, - numberOfSites: siteListSize, - }, - } - ); - } - } - bulkActionDialog = ( actionName, selectedPlugin ) => { - const { plugins, translate } = this.props; + const { plugins, translate, allSites } = this.props; const selectedPlugins = selectedPlugin ? [ selectedPlugin ] : plugins.filter( this.isSelected ); const pluginsCount = selectedPlugins.length; @@ -453,109 +360,132 @@ export class PluginsList extends Component { count: pluginsCount, }; + const dialogOptions = { + additionalClassNames: 'plugins__confirmation-modal', + }; + switch ( actionName ) { - case 'activate': + case 'activate': { + const heading = translate( + 'Activate %(pluginsCount)d plugin', + 'Activate %(pluginsCount)d plugins', + translationArgs + ); acceptDialog( -
- { this.getConfirmationText( selectedPlugins, 'activate', 'on' ) } -
, + getPluginActionDailogMessage( allSites, selectedPlugins, heading, 'activate' ), ( accepted ) => this.activateSelected( accepted ), - translate( - 'Activate %(pluginsCount)d plugin', - 'Activate %(pluginsCount)d plugins', - translationArgs - ) + heading, + null, + dialogOptions ); break; - case 'deactivate': + } + case 'deactivate': { + const heading = translate( + 'Deactivate %(pluginsCount)d plugin', + 'Deactivate %(pluginsCount)d plugins', + translationArgs + ); acceptDialog( -
- { this.getConfirmationText( selectedPlugins, 'deactivate', 'on' ) } -
, + getPluginActionDailogMessage( allSites, selectedPlugins, heading, 'deactivate' ), isJetpackIncluded ? ( accepted ) => this.deactiveAndDisconnectSelected( accepted ) : ( accepted ) => this.deactivateSelected( accepted ), - translate( - 'Deactivate %(pluginsCount)d plugin', - 'Deactivate %(pluginsCount)d plugins', - translationArgs - ) + heading, + null, + dialogOptions ); break; - case 'enableAutoupdates': + } + case 'enableAutoupdates': { + const heading = translate( + 'Enable autoupdates for %(pluginsCount)d plugin', + 'Enable autoupdates for %(pluginsCount)d plugins', + translationArgs + ); acceptDialog( -
- - { this.getConfirmationText( selectedPlugins, 'enable autoupdates for', 'on' ) } - -
, + getPluginActionDailogMessage( + allSites, + selectedPlugins, + heading, + 'enable autoupdates for' + ), ( accepted ) => this.setAutoupdateSelected( accepted ), - translate( - 'Enable autoupdates for %(pluginsCount)d plugin', - 'Enable autoupdates for %(pluginsCount)d plugins', - translationArgs - ) + heading, + null, + dialogOptions ); break; - case 'disableAutoupdates': + } + case 'disableAutoupdates': { + const heading = translate( + 'Disable autoupdates for %(pluginsCount)d plugin', + 'Disable autoupdates for %(pluginsCount)d plugins', + translationArgs + ); acceptDialog( -
- - { this.getConfirmationText( selectedPlugins, 'disable autoupdates for', 'on' ) } - -
, + getPluginActionDailogMessage( + allSites, + selectedPlugins, + heading, + 'disable autoupdates for' + ), ( accepted ) => this.unsetAutoupdateSelected( accepted ), - translate( - 'Disable autoupdates for %(pluginsCount)d plugin', - 'Disable autoupdates for %(pluginsCount)d plugins', - translationArgs - ) + heading, + null, + dialogOptions ); break; - case 'update': + } + case 'update': { + const heading = translate( + 'Update %(pluginsCount)d plugin', + 'Update %(pluginsCount)d plugins', + translationArgs + ); acceptDialog( -
- { this.getConfirmationText( selectedPlugins, 'update', 'on' ) } -
, + getPluginActionDailogMessage( allSites, selectedPlugins, heading, 'update' ), ( accepted ) => this.updateSelected( accepted ), - translate( - 'Update %(pluginsCount)d plugin', - 'Update %(pluginsCount)d plugins', - translationArgs - ) + heading, + null, + dialogOptions ); + } } }; removePluginDialog = ( selectedPlugin ) => { - const { plugins, translate } = this.props; + const { plugins, translate, allSites } = this.props; const selectedPlugins = selectedPlugin ? [ selectedPlugin ] : plugins.filter( this.isSelected ); const isJetpackIncluded = selectedPlugins.some( ( { slug } ) => slug === 'jetpack' ); - const message = ( -
- { this.getConfirmationText( selectedPlugins, 'remove', 'from' ) } - - { translate( - '{{p}}This will deactivate the plugins and delete all associated files and data.{{/p}}', - { - components: { - p:

, - }, - } - ) } - - { translate( 'Do you want to continue?' ) } -

+ const dialogOptions = { + additionalClassNames: 'plugins__confirmation-modal', + isScary: true, + }; + + const pluginsCount = selectedPlugins.length; + + const translationArgs = { + args: { pluginsCount }, + count: pluginsCount, + }; + + const heading = translate( + 'Remove %(pluginsCount)d plugin', + 'Remove %(pluginsCount)d plugins', + translationArgs ); acceptDialog( - message, + getPluginActionDailogMessage( allSites, selectedPlugins, heading, 'deactivate and delete' ), isJetpackIncluded ? ( accepted ) => this.removeSelectedWithJetpack( accepted, selectedPlugins ) : ( accepted ) => this.removeSelected( accepted, selectedPlugins ), - translate( 'Remove', { context: 'Verb. Presented to user as a label for a button.' } ) + heading, + null, + dialogOptions ); }; diff --git a/client/my-sites/plugins/style.scss b/client/my-sites/plugins/style.scss index c9bb409791b0a..9de26023c3fb2 100644 --- a/client/my-sites/plugins/style.scss +++ b/client/my-sites/plugins/style.scss @@ -1,5 +1,5 @@ -@import "@wordpress/base-styles/_breakpoints.scss"; -@import "@wordpress/base-styles/_mixins.scss"; +@import "@wordpress/base-styles/breakpoints"; +@import "@wordpress/base-styles/mixins"; @import "./grid-mixins.scss"; @import "./woocommerce-box.scss"; @@ -457,3 +457,28 @@ body.is-section-plugins header .select-dropdown__item { } } } + +.plugins__confirmation-modal { + .plugins__confirmation-modal-heading { + font-size: 1rem; + font-weight: 600; + } + + .plugins__confirmation-modal-desc { + display: block; + padding-top: 12px; + font-size: 1rem; + font-weight: 400; + } + + .dialog__action-buttons { + padding: 24px; + + button { + margin-inline-start: 16px; + padding: 7px; + font-size: 0.75rem; + line-height: 1; + } + } +} diff --git a/client/my-sites/plugins/utils.js b/client/my-sites/plugins/utils.js index 1fd7fce2860d8..62e0ebe387f00 100644 --- a/client/my-sites/plugins/utils.js +++ b/client/my-sites/plugins/utils.js @@ -1,5 +1,5 @@ import { isMagnificentLocale } from '@automattic/i18n-utils'; -import { useTranslate } from 'i18n-calypso'; +import { useTranslate, translate } from 'i18n-calypso'; import { useCallback } from 'react'; import { useSelector } from 'react-redux'; import { isUserLoggedIn } from 'calypso/state/current-user/selectors'; @@ -28,3 +28,90 @@ export function useLocalizedPlugins() { return { localizePath }; } + +// Returns translated text based on different combination of plugins and sites +const getConfirmationText = ( sites, selectedPlugins, actionText ) => { + const pluginsList = {}; + const sitesList = {}; + let pluginName; + let siteName; + + selectedPlugins.forEach( ( plugin ) => { + pluginsList[ plugin.slug ] = true; + pluginName = plugin.name || plugin.slug; + + Object.keys( plugin.sites ).forEach( ( siteId ) => { + const site = sites.find( ( s ) => s.ID === parseInt( siteId ) ); + if ( site.canUpdateFiles ) { + sitesList[ site.ID ] = true; + siteName = site.title; + } + } ); + } ); + + const pluginsListSize = Object.keys( pluginsList ).length; + const siteListSize = Object.keys( sitesList ).length; + const combination = + ( siteListSize > 1 ? 'n sites' : '1 site' ) + + ' ' + + ( pluginsListSize > 1 ? 'n plugins' : '1 plugin' ); + + switch ( combination ) { + case '1 site 1 plugin': + return translate( 'You are about to %(actionText)s %(plugin)s installed on %(site)s.', { + args: { + actionText: actionText, + plugin: pluginName, + site: siteName, + }, + } ); + + case '1 site n plugins': + return translate( + 'You are about to %(actionText)s %(numberOfPlugins)d plugins installed on %(site)s.', + { + args: { + actionText: actionText, + numberOfPlugins: pluginsListSize, + site: siteName, + }, + } + ); + + case 'n sites 1 plugin': + return translate( + 'You are about to %(actionText)s %(plugin)s installed across %(numberOfSites)d sites.', + { + args: { + actionText: actionText, + plugin: pluginName, + numberOfSites: siteListSize, + }, + } + ); + + case 'n sites n plugins': + return translate( + 'You are about to %(actionText)s %(numberOfPlugins)d plugins installed across %(numberOfSites)d sites.', + { + args: { + actionText: actionText, + numberOfPlugins: pluginsListSize, + numberOfSites: siteListSize, + }, + } + ); + } +}; + +// Returns plugin action dailog message for different action types +export const getPluginActionDailogMessage = ( sites, selectedPlugins, heading, actionText ) => { + return ( +
+
{ heading }
+ + { getConfirmationText( sites, selectedPlugins, actionText ) } + +
+ ); +}; From 7db3169a7757e0118e752407519112603de37dd1 Mon Sep 17 00:00:00 2001 From: Yashwin Date: Thu, 15 Sep 2022 11:11:55 +0530 Subject: [PATCH 2/2] Show plugin name when only one plugin is selected --- .../my-sites/plugins/plugins-list/index.jsx | 116 ++++++++---------- 1 file changed, 54 insertions(+), 62 deletions(-) diff --git a/client/my-sites/plugins/plugins-list/index.jsx b/client/my-sites/plugins/plugins-list/index.jsx index ad95bc1df7bf2..f6a672e42d1b6 100644 --- a/client/my-sites/plugins/plugins-list/index.jsx +++ b/client/my-sites/plugins/plugins-list/index.jsx @@ -355,22 +355,24 @@ export class PluginsList extends Component { const isJetpackIncluded = selectedPlugins.some( ( { slug } ) => slug === 'jetpack' ); - const translationArgs = { - args: { pluginsCount }, - count: pluginsCount, - }; - const dialogOptions = { additionalClassNames: 'plugins__confirmation-modal', + ...( actionName === 'remove' && { isScary: true } ), }; + let pluginName; + const hasOnePlugin = pluginsCount === 1; + + if ( hasOnePlugin ) { + const [ { name, slug } ] = selectedPlugins; + pluginName = name || slug; + } + switch ( actionName ) { case 'activate': { - const heading = translate( - 'Activate %(pluginsCount)d plugin', - 'Activate %(pluginsCount)d plugins', - translationArgs - ); + const heading = hasOnePlugin + ? translate( 'Activate %(pluginName)s', { args: { pluginName } } ) + : translate( 'Activate %(pluginsCount)d plugins', { args: { pluginsCount } } ); acceptDialog( getPluginActionDailogMessage( allSites, selectedPlugins, heading, 'activate' ), ( accepted ) => this.activateSelected( accepted ), @@ -381,11 +383,9 @@ export class PluginsList extends Component { break; } case 'deactivate': { - const heading = translate( - 'Deactivate %(pluginsCount)d plugin', - 'Deactivate %(pluginsCount)d plugins', - translationArgs - ); + const heading = hasOnePlugin + ? translate( 'Deactivate %(pluginName)s', { args: { pluginName } } ) + : translate( 'Deactivate %(pluginsCount)d plugins', { args: { pluginsCount } } ); acceptDialog( getPluginActionDailogMessage( allSites, selectedPlugins, heading, 'deactivate' ), isJetpackIncluded @@ -398,11 +398,11 @@ export class PluginsList extends Component { break; } case 'enableAutoupdates': { - const heading = translate( - 'Enable autoupdates for %(pluginsCount)d plugin', - 'Enable autoupdates for %(pluginsCount)d plugins', - translationArgs - ); + const heading = hasOnePlugin + ? translate( 'Enable autoupdate for %(pluginName)s', { args: { pluginName } } ) + : translate( 'Enable autoupdates for %(pluginsCount)d plugins', { + args: { pluginsCount }, + } ); acceptDialog( getPluginActionDailogMessage( allSites, @@ -418,11 +418,11 @@ export class PluginsList extends Component { break; } case 'disableAutoupdates': { - const heading = translate( - 'Disable autoupdates for %(pluginsCount)d plugin', - 'Disable autoupdates for %(pluginsCount)d plugins', - translationArgs - ); + const heading = hasOnePlugin + ? translate( 'Disable autoupdate for %(pluginName)s', { args: { pluginName } } ) + : translate( 'Disable autoupdates for %(pluginsCount)d plugins', { + args: { pluginsCount }, + } ); acceptDialog( getPluginActionDailogMessage( allSites, @@ -438,11 +438,11 @@ export class PluginsList extends Component { break; } case 'update': { - const heading = translate( - 'Update %(pluginsCount)d plugin', - 'Update %(pluginsCount)d plugins', - translationArgs - ); + const heading = hasOnePlugin + ? translate( 'Update %(pluginName)s', { args: { pluginName } } ) + : translate( 'Update %(pluginsCount)d plugins', { + args: { pluginsCount }, + } ); acceptDialog( getPluginActionDailogMessage( allSites, selectedPlugins, heading, 'update' ), ( accepted ) => this.updateSelected( accepted ), @@ -450,43 +450,35 @@ export class PluginsList extends Component { null, dialogOptions ); + break; + } + case 'remove': { + const heading = hasOnePlugin + ? translate( 'Remove %(pluginName)s', { args: { pluginName } } ) + : translate( 'Remove %(pluginsCount)d plugins', { + args: { pluginsCount }, + } ); + acceptDialog( + getPluginActionDailogMessage( + allSites, + selectedPlugins, + heading, + 'deactivate and delete' + ), + isJetpackIncluded + ? ( accepted ) => this.removeSelectedWithJetpack( accepted, selectedPlugins ) + : ( accepted ) => this.removeSelected( accepted, selectedPlugins ), + heading, + null, + dialogOptions + ); + break; } } }; removePluginDialog = ( selectedPlugin ) => { - const { plugins, translate, allSites } = this.props; - - const selectedPlugins = selectedPlugin ? [ selectedPlugin ] : plugins.filter( this.isSelected ); - const isJetpackIncluded = selectedPlugins.some( ( { slug } ) => slug === 'jetpack' ); - - const dialogOptions = { - additionalClassNames: 'plugins__confirmation-modal', - isScary: true, - }; - - const pluginsCount = selectedPlugins.length; - - const translationArgs = { - args: { pluginsCount }, - count: pluginsCount, - }; - - const heading = translate( - 'Remove %(pluginsCount)d plugin', - 'Remove %(pluginsCount)d plugins', - translationArgs - ); - - acceptDialog( - getPluginActionDailogMessage( allSites, selectedPlugins, heading, 'deactivate and delete' ), - isJetpackIncluded - ? ( accepted ) => this.removeSelectedWithJetpack( accepted, selectedPlugins ) - : ( accepted ) => this.removeSelected( accepted, selectedPlugins ), - heading, - null, - dialogOptions - ); + this.bulkActionDialog( 'remove', selectedPlugin ); }; removeSelected = ( accepted, selectedPlugins ) => {