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

Preview Sites: Add Clear button for expired sites with related styling #913

Merged
merged 8 commits into from
Feb 11, 2025
1 change: 1 addition & 0 deletions src/components/content-tab-previews.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ export function ContentTabPreviews( { selectedSite }: ContentTabPreviewsProps )
) }
{ snapshotsOnSite
.filter( ( snapshot ) => ! snapshot.isLoading )
.sort( ( a, b ) => b.date - a.date )
ivan-ottinger marked this conversation as resolved.
Show resolved Hide resolved
.map( ( snapshot ) => (
<PreviewSiteRow
snapshot={ snapshot }
Expand Down
52 changes: 36 additions & 16 deletions src/modules/preview-site/components/preview-site-row.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { useExpirationDate } from 'src/hooks/use-expiration-date';
import { useFormatLocalizedTimestamps } from 'src/hooks/use-format-localized-timestamps';
import { useSnapshots } from 'src/hooks/use-snapshots';
import { useUpdateDemoSite } from 'src/hooks/use-update-demo-site';
import { cx } from 'src/lib/cx';
import { getIpcApi } from 'src/lib/get-ipc-api';
import { PreviewActionButtonsMenu } from 'src/modules/preview-site/components/preview-action-buttons-menu';
import { ProgressRow } from 'src/modules/preview-site/components/progress-row';
Expand All @@ -32,8 +33,8 @@ export function PreviewSiteRow( {
}: PreviewSiteRowProps ) {
const { __ } = useI18n();
const { url, date, isDeleting } = snapshot;
const { countDown } = useExpirationDate( date );
const { fetchSnapshotUsage } = useSnapshots();
const { countDown, isExpired } = useExpirationDate( date );
const { fetchSnapshotUsage, removeSnapshot } = useSnapshots();
const { isDemoSiteUpdating } = useUpdateDemoSite();
const isPreviewSiteUpdating = isDemoSiteUpdating( snapshot.atomicSiteId );
const { formatRelativeTime } = useFormatLocalizedTimestamps();
Expand Down Expand Up @@ -93,20 +94,29 @@ export function PreviewSiteRow( {
<div className="flex items-center px-8 py-6">
<div className="w-[51%]">
<div className="flex items-center">
<div className="text-[13px] leading-5 line-clamp-1 break-all">
<div
className={ cx(
'text-[13px] leading-5 line-clamp-1 break-all',
isExpired && 'line-through text-[#757575]'
Copy link
Member

Choose a reason for hiding this comment

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

I've investigated if we have the color text-a8c-gray-700 - #757575 , but it seems we don't have it. I've tried updating automattic/color-studio but the new version 4.1.0 doesn't include it either.
I think it's ok to leave it as it is, since we are using that color in the rest of the app, but we could consider adding a new variable in tailwind config.

Something like:

a8cToTailwindColors[ ${ PREFIX }-gray-700 ] = '#757575'; // Gray 700

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's a great idea! Thanks! I have added the variable and replaced all occurrences of #757575 in the latest commit: 323002b.

) }
>
{ /* translators: %s: Site name (e.g. "My Site Preview") */ }
{ snapshot.name || sprintf( __( '%s Preview' ), selectedSite.name ) }
</div>
</div>
<Button
variant="link"
className="!text-a8c-gray-70 hover:!text-a8c-blueberry max-w-[100%]"
onClick={ () => {
getIpcApi().openURL( urlWithHTTPS );
} }
disabled={ isExpired }
className={ cx(
'!text-a8c-gray-70 max-w-[100%]',
isExpired ? 'pointer-events-none' : 'hover:!text-a8c-blueberry'
) }
onClick={ () => getIpcApi().openURL( urlWithHTTPS ) }
>
<span className="truncate">{ urlWithHTTPS }</span>
<ArrowIcon />
<span className={ cx( 'truncate', isExpired && 'line-through text-[#757575]' ) }>
{ urlWithHTTPS }
</span>
{ ! isExpired && <ArrowIcon /> }
</Button>
</div>
<div className="flex ml-auto">
Expand All @@ -122,13 +132,23 @@ export function PreviewSiteRow( {
</div>
<div className="w-[100px] text-[#757575] flex items-center pl-4">{ countDown }</div>
<div className="w-[60px] flex justify-end">
<PreviewActionButtonsMenu
snapshot={ snapshot }
selectedSite={ selectedSite }
disabledUpdate={ disabledUpdate }
updateButtonTooltipContent={ updateButtonTooltipContent }
showUpdateTooltip={ showUpdateTooltip }
/>
{ isExpired ? (
<Button
variant="link"
onClick={ () => removeSnapshot( snapshot ) }
className={ '!text-a8c-blueberry hover:!text-a8c-red-50' }
>
{ __( 'Clear' ) }
</Button>
) : (
<PreviewActionButtonsMenu
snapshot={ snapshot }
selectedSite={ selectedSite }
disabledUpdate={ disabledUpdate }
updateButtonTooltipContent={ updateButtonTooltipContent }
showUpdateTooltip={ showUpdateTooltip }
/>
) }
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { render, screen } from '@testing-library/react';
import { useExpirationDate } from 'src/hooks/use-expiration-date';
import { PreviewSiteRow } from '../preview-site-row';

jest.mock( 'src/hooks/use-snapshots', () => ( {
useSnapshots: jest.fn().mockReturnValue( {
removeSnapshot: jest.fn(),
fetchSnapshotUsage: jest.fn(),
} ),
} ) );

jest.mock( 'src/hooks/use-expiration-date', () => ( {
useExpirationDate: jest.fn().mockReturnValue( {
countDown: '5 days',
isExpired: false,
} ),
} ) );

jest.mock( 'src/hooks/use-format-localized-timestamps', () => ( {
useFormatLocalizedTimestamps: jest.fn().mockReturnValue( {
formatRelativeTime: jest.fn().mockReturnValue( '2 hours' ),
} ),
} ) );

describe( 'PreviewSiteRow', () => {
const mockSnapshot = {
atomicSiteId: 123,
localSiteId: 'db30ac2b-1d8f-4df2-a171-1b9ea3bc149d',
url: 'shad-of-cellos.wp.build',
date: 123456789,
name: 'Test Preview 1',
sequence: 1,
};

const mockSelectedSite: StoppedSiteDetails = {
id: '456',
name: 'Test',
path: '/test/path',
phpVersion: '8.2',
running: false,
};

beforeEach( () => {
jest.clearAllMocks();
} );

it( 'renders PreviewActionButtonsMenu when preview site is not expired', () => {
render(
<PreviewSiteRow
snapshot={ mockSnapshot }
selectedSite={ mockSelectedSite }
disabledUpdate={ false }
/>
);

expect( screen.getByRole( 'button', { name: 'Preview actions' } ) ).toBeInTheDocument();
expect( screen.queryByRole( 'button', { name: 'Clear' } ) ).not.toBeInTheDocument();
} );

it( 'renders Clear button instead of PreviewActionButtonsMenu when preview site is expired', () => {
( useExpirationDate as jest.Mock ).mockReturnValueOnce( {
countDown: 'Expired',
isExpired: true,
} );

render(
<PreviewSiteRow
snapshot={ mockSnapshot }
selectedSite={ mockSelectedSite }
disabledUpdate={ false }
/>
);

expect( screen.queryByRole( 'button', { name: 'Preview actions' } ) ).not.toBeInTheDocument();
expect( screen.getByRole( 'button', { name: 'Clear' } ) ).toBeInTheDocument();
} );

it( 'applies line-through style when preview site is expired', () => {
( useExpirationDate as jest.Mock ).mockReturnValueOnce( {
countDown: 'Expired',
isExpired: true,
} );

render(
<PreviewSiteRow
snapshot={ mockSnapshot }
selectedSite={ mockSelectedSite }
disabledUpdate={ false }
/>
);

const siteName = screen.getByText( mockSnapshot.name );
const siteUrl = screen.getByText( `https://${ mockSnapshot.url }` );

expect( siteName.className ).toContain( 'line-through' );
expect( siteUrl.className ).toContain( 'line-through' );
} );
} );
Loading