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

Plugin Management: Implement multi-site(large screen) view for the plugin management #66219

Merged
merged 8 commits into from
Aug 8, 2022
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
1 change: 1 addition & 0 deletions client/components/section-nav/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ class SectionNav extends Component {

className = classNames( 'section-nav', this.props.className, {
'is-open': this.state.mobileOpen,
'section-nav-updated': this.props.applyUpdatedStyles,
'has-pinned-items': this.hasPinnedSearch || this.props.hasPinnedItems,
} );

Expand Down
27 changes: 27 additions & 0 deletions client/components/section-nav/item.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
@import '@wordpress/base-styles/breakpoints';
@import '@wordpress/base-styles/mixins';

.section-nav-tab .count {
margin-left: 8px;
}
Expand Down Expand Up @@ -129,3 +132,27 @@
width: auto;
}
}
.section-nav-updated.section-nav {
.is-selected.section-nav-tab .section-nav-tab__link .count {
color: var( --color-primary );
}
.section-nav__mobile-header {
.count {
margin-inline-start: 8px;
}
.gridicon {
margin-inline-start: auto;
}
}
.count {
background: var( --color-primary-5 );
border-radius: 3px; // stylelint-disable-line scales/radii
border: none;
font-weight: normal;
}
margin-bottom: 0;
@include break-large() {
background: var( --color-primary-0 );
box-shadow: none;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ export default function SitesOverview(): ReactElement {
</div>
</div>
<SectionNav
applyUpdatedStyles
yashwin marked this conversation as resolved.
Show resolved Hide resolved
selectedText={
<span>
{ selectedItem.label }
Expand All @@ -148,7 +149,6 @@ export default function SitesOverview(): ReactElement {
}
selectedCount={ selectedItem.count }
className={ classNames(
'sites-overview__section-nav',
isMobile &&
highlightTab &&
selectedItem.key === 'favorites' &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,30 +254,6 @@
.sites-overview__status-warning {
border-color: var( --studio-yellow-20 );
}
.sites-overview__section-nav.section-nav {
.is-selected.section-nav-tab .section-nav-tab__link .count {
color: var( --color-primary );
}
.section-nav__mobile-header {
.count {
margin-inline-start: 8px;
}
.gridicon {
margin-inline-start: auto;
}
}
.count {
background: var( --color-primary-5 );
border-radius: 3px; // stylelint-disable-line scales/radii
border: none;
font-weight: normal;
}
margin-bottom: 0;
@include break-large() {
background: unset;
box-shadow: none;
}
}
@keyframes highlight-tab-animation {
0% {
background: var( --color-neutral-70 );
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
.plugins-overview__container {
padding: 0 16px;
padding: 6px 0;
}
.plugins-overview__logo {
fill: var( --color-neutral-10 );
Expand Down
119 changes: 94 additions & 25 deletions client/my-sites/plugins/main.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
import { Button } from '@automattic/components';
import { subscribeIsWithinBreakpoint, isWithinBreakpoint } from '@automattic/viewport';
import { Icon, upload } from '@wordpress/icons';
import classNames from 'classnames';
import { localize } from 'i18n-calypso';
import { capitalize, find, flow, isEmpty } from 'lodash';
import page from 'page';
Expand Down Expand Up @@ -139,8 +140,10 @@ export class PluginsMain extends Component {
}

getFilters() {
const { translate } = this.props;
const siteFilter = this.props.selectedSiteSlug ? '/' + this.props.selectedSiteSlug : '';
const { translate, search } = this.props;
const siteFilter = `${ this.props.selectedSiteSlug ? '/' + this.props.selectedSiteSlug : '' }${
search ? '?s=' + search : ''
}`;

return [
{
Expand Down Expand Up @@ -303,10 +306,11 @@ export class PluginsMain extends Component {
}

renderPluginsContent() {
const { search } = this.props;
const { search, isJetpackCloud } = this.props;

const currentPlugins = this.getCurrentPlugins();
const showInstalledPluginList = ! isEmpty( currentPlugins ) || this.isFetchingPlugins();
const showInstalledPluginList =
isJetpackCloud || ! isEmpty( currentPlugins ) || this.isFetchingPlugins();

if ( ! showInstalledPluginList && ! search ) {
const emptyContentData = this.getEmptyContentData();
Expand All @@ -328,6 +332,9 @@ export class PluginsMain extends Component {
plugins={ currentPlugins }
pluginUpdateCount={ this.props.pluginUpdateCount }
isPlaceholder={ this.shouldShowPluginListPlaceholders() }
isLoading={ this.props.requestingPluginsForSites }
isJetpackCloud={ this.props.isJetpackCloud }
searchTerm={ search }
/>
);

Expand Down Expand Up @@ -422,16 +429,23 @@ export class PluginsMain extends Component {
if ( 'updates' === filterItem.id ) {
attr.count = this.props.pluginUpdateCount;
}
if ( 'all' === filterItem.id ) {
attr.count = this.props.allPluginsCount;
}
return <NavItem { ...attr }>{ filterItem.title }</NavItem>;
} );

return (
<Main wideLayout>
<DocumentHead title={ this.props.translate( 'Plugins', { textOnly: true } ) } />
const pageTitle = this.props.translate( 'Plugins', { textOnly: true } );

const { isJetpackCloud } = this.props;
vitozev marked this conversation as resolved.
Show resolved Hide resolved

const content = (
<>
<DocumentHead title={ pageTitle } />
<QueryJetpackPlugins siteIds={ this.props.siteIds } />
<QuerySiteFeatures siteIds={ this.props.siteIds } />
{ this.renderPageViewTracking() }
{ this.props.shouldDisplayNavigationHeader && (
{ ! isJetpackCloud && (
<FixedNavigationHeader
className="plugins__page-heading"
navigationItems={ this.getNavigationItems() }
Expand All @@ -442,25 +456,78 @@ export class PluginsMain extends Component {
</div>
</FixedNavigationHeader>
) }
<div className="plugins__main">
<div className="plugins__main-header">
<SectionNav selectedText={ this.getSelectedText() }>
<NavTabs>{ navItems }</NavTabs>
<Search
pinned
fitsContainer
onSearch={ this.props.doSearch }
initialValue={ this.props.search }
ref={ `url-search` }
analyticsGroup="Plugins"
placeholder={ this.getSearchPlaceholder() }
/>
</SectionNav>
<div className={ classNames( { 'plugins__top-container': isJetpackCloud } ) }>
<div
className={ classNames( {
'plugins__content-wrapper': isJetpackCloud,
} ) }
>
{ isJetpackCloud && (
<div className="plugins__page-title-container">
<h2 className="plugins__page-title">{ pageTitle }</h2>
<div className="plugins__page-subtitle">
{ this.props.selectedSite
? this.props.translate( 'Manage all plugins installed on %(selectedSite)s', {
args: {
selectedSite: this.props.selectedSite.domain,
},
} )
: this.props.translate( 'Manage plugins installed on all sites' ) }
</div>
</div>
) }
<div
className={ classNames( 'plugins__main', {
'plugins__jetpack-cloud': isJetpackCloud,
} ) }
>
<div className="plugins__main-header">
<SectionNav
applyUpdatedStyles={ isJetpackCloud }
selectedText={ this.getSelectedText() }
>
<NavTabs>{ navItems }</NavTabs>
{ ! isJetpackCloud && (
<Search
pinned
fitsContainer
onSearch={ this.props.doSearch }
initialValue={ this.props.search }
ref={ `url-search` }
analyticsGroup="Plugins"
placeholder={ this.getSearchPlaceholder() }
/>
) }
</SectionNav>
</div>
</div>
</div>
</div>
{ this.renderPluginsContent() }
</Main>
{ isJetpackCloud ? (
<div className="plugins__main-content">
<div className="plugins__content-wrapper">
<div className="plugins__search">
<Search
hideFocus
isOpen
onSearch={ this.props.doSearch }
initialValue={ this.props.search }
hideClose={ ! this.props.search }
ref={ `url-search` }
analyticsGroup="Plugins"
placeholder={ this.props.translate( 'Search plugins' ) }
/>
</div>
{ this.renderPluginsContent() }
</div>
</div>
) : (
this.renderPluginsContent()
) }
</>
);

return isJetpackCloud ? content : <Main wideLayout>{ content }</Main>;
}
}

Expand All @@ -475,6 +542,7 @@ export default flow(
const visibleSiteIds = siteObjectsToSiteIds( getVisibleSites( sites ) ) ?? [];
const siteIds = siteObjectsToSiteIds( sites ) ?? [];
const pluginsWithUpdates = getPlugins( state, siteIds, 'updates' );
const allPlugins = getPlugins( state, siteIds, 'all' );
const jetpackNonAtomic =
isJetpackSite( state, selectedSiteId ) && ! isAtomicSite( state, selectedSiteId );
const hasManagePlugins =
Expand All @@ -500,6 +568,7 @@ export default flow(
currentPlugins: getPlugins( state, siteIds, filter ),
currentPluginsOnVisibleSites: getPlugins( state, visibleSiteIds, filter ),
pluginUpdateCount: pluginsWithUpdates && pluginsWithUpdates.length,
allPluginsCount: allPlugins && allPlugins.length,
requestingPluginsForSites: isRequestingForSites( state, siteIds ),
updateableJetpackSites: getUpdateableJetpackSites( state ),
userCanManagePlugins: selectedSiteId
Expand All @@ -508,7 +577,7 @@ export default flow(
hasManagePlugins: hasManagePlugins,
hasUploadPlugins: hasUploadPlugins,
hasInstallPurchasedPlugins: hasInstallPurchasedPlugins,
shouldDisplayNavigationHeader: ! isJetpackCloud,
isJetpackCloud,
};
},
{ wporgFetchPluginData, recordTracksEvent, recordGoogleEvent }
Expand Down
53 changes: 53 additions & 0 deletions client/my-sites/plugins/plugin-management-v2/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { useTranslate } from 'i18n-calypso';
import { ReactElement } from 'react';
import PluginsTable from './plugins-table';
import type { Plugin } from './types';
import type { SiteData } from 'calypso/state/ui/selectors/site-data';

import './style.scss';

interface Props {
plugins: Array< Plugin >;
isLoading: boolean;
selectedSite: SiteData;
searchTerm: string;
}
export default function PluginManagementV2( {
plugins,
isLoading,
selectedSite,
searchTerm,
}: Props ): ReactElement {
const translate = useTranslate();
const columns = [
{
key: 'plugin',
title: translate( 'Installed Plugins' ),
},
{
key: 'sites',
title: translate( 'Sites' ),
smallColumn: true,
colSpan: 2,
},
];

if ( ! plugins.length && ! isLoading ) {
let emptyStateMessage = translate( 'No plugins found' );
if ( searchTerm ) {
emptyStateMessage = translate( 'No results found. Please try refining your search.' );
}
return <div className="plugin-management-v2__no-sites">{ emptyStateMessage }</div>;
}

return (
<div className="plugin-management-v2__main-content-container">
<PluginsTable
items={ plugins }
columns={ columns }
isLoading={ isLoading }
selectedSite={ selectedSite }
/>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Gridicon, Button } from '@automattic/components';
import type { Plugin } from '../types';
import type { ReactChild, ReactElement } from 'react';

import './style.scss';

interface Props {
item: Plugin;
columnKey: string;
}

export default function PluginRowFormatter( { item, columnKey }: Props ): ReactElement | any {
const PluginDetailsButton = ( props: { className: string; children: ReactChild } ) => {
return <Button borderless compact href={ `/plugins/${ item.slug }` } { ...props } />;
};

switch ( columnKey ) {
case 'plugin':
return (
<span className="plugin-row-formatter__plugin-name-container">
{ item.icon ? (
<img
className="plugin-row-formatter__plugin-icon"
src={ item.icon }
alt={ item.name }
/>
) : (
<Gridicon className="plugin-row-formatter__plugin-icon has-opacity" icon="plugins" />
) }
<PluginDetailsButton className="plugin-row-formatter__plugin-name">
{ item.name }
</PluginDetailsButton>
<span className="plugin-row-formatter__overlay"></span>
</span>
);
case 'sites':
return (
<PluginDetailsButton className="plugin-row-formatter__sites-count-button">
{ Object.keys( item.sites ).length }
</PluginDetailsButton>
);
}
}
Loading