Skip to content
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
29 changes: 4 additions & 25 deletions src/components/content-tab-settings.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { DropdownMenu, MenuGroup, Button } from '@wordpress/components';
import { moreVertical } from '@wordpress/icons';
import { useI18n } from '@wordpress/react-i18n';
import { PropsWithChildren, useEffect, useRef } from 'react';
import { PropsWithChildren } from 'react';
import { CopyTextButton } from 'src/components/copy-text-button';
import DeleteSite from 'src/components/delete-site';
import { useGetWpVersion } from 'src/hooks/use-get-wp-version';
import { getIpcApi } from 'src/lib/get-ipc-api';
import { decodePassword } from 'src/lib/passwords';
import EditSiteDetails, { EditSiteDetailsRef } from 'src/modules/site-settings/edit-site-details';
import EditSiteDetails from 'src/modules/site-settings/edit-site-details';
import { useAppDispatch } from 'src/stores';
import {
certificateTrustApi,
Expand All @@ -16,8 +16,6 @@ import {

interface ContentTabSettingsProps {
selectedSite: SiteDetails;
shouldOpenEditModal?: boolean;
onEditModalOpened?: () => void;
}

function SettingsRow( { children, label }: PropsWithChildren< { label: string } > ) {
Expand All @@ -31,15 +29,10 @@ function SettingsRow( { children, label }: PropsWithChildren< { label: string }
);
}

export function ContentTabSettings( {
selectedSite,
shouldOpenEditModal,
onEditModalOpened,
}: ContentTabSettingsProps ) {
export function ContentTabSettings( { selectedSite }: ContentTabSettingsProps ) {
const dispatch = useAppDispatch();
const { __ } = useI18n();
const { data: isCertificateTrusted } = useCheckCertificateTrustQuery();
const editSiteRef = useRef< EditSiteDetailsRef >( null );
const username = 'admin';
// Empty strings account for legacy sites lacking a stored password.
const storedPassword = decodePassword( selectedSite.adminPassword ?? '' );
Expand All @@ -57,28 +50,14 @@ export function ContentTabSettings( {
await dispatch( certificateTrustApi.util.invalidateTags( [ 'CertificateTrust' ] ) );
};

// Open edit modal when requested from context menu
useEffect( () => {
if ( shouldOpenEditModal ) {
setTimeout( () => {
editSiteRef.current?.openModal();
onEditModalOpened?.();
}, 100 );
}
}, [ shouldOpenEditModal, onEditModalOpened ] );

return (
<div className="p-8 ltr:pr-4 rtl:pl-4">
<div className="flex justify-between items-center mb-4">
<h3 role="heading" className="text-black text-sm font-semibold">
{ __( 'Site details' ) }
</h3>
<div className="flex items-center gap-1">
<EditSiteDetails
ref={ editSiteRef }
currentWpVersion={ wpVersion }
onSave={ refreshWpVersion }
/>
<EditSiteDetails currentWpVersion={ wpVersion } onSave={ refreshWpVersion } />
<DropdownMenu
icon={ moreVertical }
label={ __( 'More options' ) }
Expand Down
35 changes: 2 additions & 33 deletions src/components/site-content-tabs.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { TabPanel } from '@wordpress/components';
import { useI18n } from '@wordpress/react-i18n';
import { useEffect, useState } from 'react';
import { ContentTabAssistant } from 'src/components/content-tab-assistant';
import { ContentTabImportExport } from 'src/components/content-tab-import-export';
import { ContentTabOverview } from 'src/components/content-tab-overview';
Expand All @@ -17,34 +16,10 @@ import { cx } from 'src/lib/cx';
import { ContentTabSync } from 'src/modules/sync';

export function SiteContentTabs() {
const { selectedSite, data: localSites, setSelectedSiteId } = useSiteDetails();
const { selectedSite, data: localSites } = useSiteDetails();
const { importState } = useImportExport();
const { tabs, selectedTab, setSelectedTab } = useContentTabs();
const { __ } = useI18n();
const [ shouldOpenEditModal, setShouldOpenEditModal ] = useState( false );

useEffect( () => {
const handleEditSiteRequest = ( event: CustomEvent ) => {
const { siteId } = event.detail;
const targetSite = localSites.find( ( site ) => site.id === siteId );

if ( ! targetSite ) {
return;
}

if ( siteId !== selectedSite?.id ) {
setSelectedSiteId( siteId );
}

setSelectedTab( 'settings' );
setShouldOpenEditModal( true );
};

window.addEventListener( 'edit-site-request', handleEditSiteRequest as EventListener );
return () => {
window.removeEventListener( 'edit-site-request', handleEditSiteRequest as EventListener );
};
}, [ selectedSite?.id, localSites, setSelectedTab, setSelectedSiteId ] );

if ( ! localSites.length ) {
return <EmptyStudio />;
Expand Down Expand Up @@ -87,13 +62,7 @@ export function SiteContentTabs() {
{ name === 'overview' && <ContentTabOverview selectedSite={ selectedSite } /> }
{ name === 'previews' && <ContentTabPreviews selectedSite={ selectedSite } /> }
{ name === 'sync' && <ContentTabSync selectedSite={ selectedSite } /> }
{ name === 'settings' && (
<ContentTabSettings
selectedSite={ selectedSite }
shouldOpenEditModal={ shouldOpenEditModal }
onEditModalOpened={ () => setShouldOpenEditModal( false ) }
/>
) }
{ name === 'settings' && <ContentTabSettings selectedSite={ selectedSite } /> }
{ name === 'assistant' && <ContentTabAssistant selectedSite={ selectedSite } /> }
{ name === 'import-export' && <ContentTabImportExport selectedSite={ selectedSite } /> }
</div>
Expand Down
23 changes: 16 additions & 7 deletions src/components/site-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { __, sprintf } from '@wordpress/i18n';
import { useEffect } from 'react';
import { Tooltip } from 'src/components/tooltip';
import { useSyncSites } from 'src/hooks/sync-sites';
import { useContentTabs } from 'src/hooks/use-content-tabs';
import { useImportExport } from 'src/hooks/use-import-export';
import { useSiteDetails } from 'src/hooks/use-site-details';
import { isMac, isWindows } from 'src/lib/app-globals';
Expand Down Expand Up @@ -113,8 +114,16 @@ function ButtonToRun( { running, id, name }: Pick< SiteDetails, 'running' | 'id'
);
}
function SiteItem( { site }: { site: SiteDetails } ) {
const { selectedSite, setSelectedSiteId, startServer, stopServer, deleteSite, loadingServer } =
useSiteDetails();
const {
selectedSite,
setSelectedSiteId,
startServer,
stopServer,
deleteSite,
loadingServer,
setIsEditModalOpen,
} = useSiteDetails();
const { setSelectedTab } = useContentTabs();
const isSelected = site === selectedSite;
const { isSiteImporting, isSiteExporting } = useImportExport();
const { isSiteIdPulling } = useSyncSites();
Expand Down Expand Up @@ -203,11 +212,11 @@ function SiteItem( { site }: { site: SiteDetails } ) {
} )();
break;
case 'edit-site':
window.dispatchEvent(
new CustomEvent( 'edit-site-request', {
detail: { siteId: site.id },
} )
);
if ( site.id !== selectedSite?.id ) {
setSelectedSiteId( site.id );
}
setSelectedTab( 'settings' );
setIsEditModalOpen( true );
Comment on lines +215 to +219
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved the logic to select the site, the settings tab and open the modal right to the place it triggers the event.

break;
case 'delete': {
const DELETE_BUTTON_INDEX = 0;
Expand Down
49 changes: 49 additions & 0 deletions src/components/tests/content-tab-settings.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -267,13 +267,28 @@ describe( 'ContentTabSettings', () => {
updateSite,
startServer,
stopServer,
isEditModalOpen: false,
setIsEditModalOpen: jest.fn(),
} );

const { rerender } = renderWithProvider(
<ContentTabSettings selectedSite={ selectedSite } />
);
expect( screen.getByText( '8.3' ) ).toBeVisible();
await user.click( screen.getByRole( 'button', { name: 'Edit site' } ) );
( useSiteDetails as jest.Mock ).mockReturnValue( {
selectedSite: { ...selectedSite, running: false } as SiteDetails,
updateSite,
startServer,
stopServer,
isEditModalOpen: true,
setIsEditModalOpen: jest.fn(),
} );
rerenderWithProvider( rerender, <ContentTabSettings selectedSite={ selectedSite } /> );
await waitFor( () => {
expect( screen.getByRole( 'dialog' ) ).toBeInTheDocument();
} );

const dialog = screen.getByRole( 'dialog' );
expect( dialog ).toBeVisible();
await user.selectOptions( within( dialog ).getByLabelText( 'PHP version' ), '8.2' );
Expand All @@ -283,6 +298,16 @@ describe( 'ContentTabSettings', () => {
} )
);

( useSiteDetails as jest.Mock ).mockReturnValue( {
selectedSite: { ...selectedSite, running: false } as SiteDetails,
updateSite,
startServer,
stopServer,
isEditModalOpen: false,
setIsEditModalOpen: jest.fn(),
} );
rerenderWithProvider( rerender, <ContentTabSettings selectedSite={ selectedSite } /> );

await waitFor( () => {
expect( updateSite ).toHaveBeenCalledWith(
expect.objectContaining( { phpVersion: '8.2' } )
Expand Down Expand Up @@ -313,13 +338,27 @@ describe( 'ContentTabSettings', () => {
updateSite,
startServer,
stopServer,
isEditModalOpen: false,
setIsEditModalOpen: jest.fn(),
} );

const { rerender } = renderWithProvider(
<ContentTabSettings selectedSite={ selectedSite } />
);
expect( screen.getByText( '8.3' ) ).toBeVisible();
await user.click( screen.getByRole( 'button', { name: 'Edit site' } ) );
( useSiteDetails as jest.Mock ).mockReturnValue( {
selectedSite: { ...selectedSite, running: true } as SiteDetails,
updateSite,
startServer,
stopServer,
isEditModalOpen: true,
setIsEditModalOpen: jest.fn(),
} );
rerenderWithProvider( rerender, <ContentTabSettings selectedSite={ selectedSite } /> );
await waitFor( () => {
expect( screen.getByRole( 'dialog' ) ).toBeInTheDocument();
} );
const dialog = screen.getByRole( 'dialog' );
expect( dialog ).toBeVisible();
await user.selectOptions( within( dialog ).getByLabelText( 'PHP version' ), '8.2' );
Expand All @@ -329,6 +368,16 @@ describe( 'ContentTabSettings', () => {
} )
);

( useSiteDetails as jest.Mock ).mockReturnValue( {
selectedSite: { ...selectedSite, running: true } as SiteDetails,
updateSite,
startServer,
stopServer,
isEditModalOpen: false,
setIsEditModalOpen: jest.fn(),
} );
rerenderWithProvider( rerender, <ContentTabSettings selectedSite={ selectedSite } /> );

await waitFor( () => {
expect( updateSite ).toHaveBeenCalledWith(
expect.objectContaining( { phpVersion: '8.2' } )
Expand Down
10 changes: 10 additions & 0 deletions src/hooks/use-site-details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ interface SiteDetailsContext {
isDeleting: boolean;
uploadingSites: { [ siteId: string ]: boolean };
setUploadingSites: React.Dispatch< React.SetStateAction< { [ siteId: string ]: boolean } > >;
isEditModalOpen: boolean;
setIsEditModalOpen: React.Dispatch< React.SetStateAction< boolean > >;
}

const defaultContext: SiteDetailsContext = {
Expand All @@ -58,6 +60,8 @@ const defaultContext: SiteDetailsContext = {
isDeleting: false,
uploadingSites: {},
setUploadingSites: () => undefined,
isEditModalOpen: false,
setIsEditModalOpen: () => undefined,
};

export const siteDetailsContext = createContext< SiteDetailsContext >( defaultContext );
Expand Down Expand Up @@ -462,6 +466,8 @@ export function SiteDetailsProvider( { children }: SiteDetailsProviderProps ) {
setData( data.map( ( site ) => ( site.running ? { ...site, running: false } : site ) ) );
}, [ data ] );

const [ isEditModalOpen, setIsEditModalOpen ] = useState( false );

const context = useMemo(
() => ( {
selectedSite: data.find( ( site ) => site.id === selectedSiteId ) || firstSite,
Expand All @@ -478,6 +484,8 @@ export function SiteDetailsProvider( { children }: SiteDetailsProviderProps ) {
loadingSites,
uploadingSites,
setUploadingSites,
isEditModalOpen,
setIsEditModalOpen,
} ),
[
data,
Expand All @@ -494,6 +502,8 @@ export function SiteDetailsProvider( { children }: SiteDetailsProviderProps ) {
isDeleting,
loadingSites,
uploadingSites,
isEditModalOpen,
setIsEditModalOpen,
]
);

Expand Down
Loading