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

feat: Add confirmation to Mac Next Steps card #1300

Merged
merged 10 commits into from
Dec 12, 2024
20 changes: 20 additions & 0 deletions special-pages/pages/new-tab/app/components/Icons.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,23 @@ export function Cross() {
</svg>
);
}

export function CheckColor() {
return (
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<path fill="#4CBA3C" d="M15.5 8a7.5 7.5 0 1 1-15 0 7.5 7.5 0 0 1 15 0" />
<path
fill="#fff"
fill-rule="evenodd"
d="M11.844 5.137a.5.5 0 0 1 .019.707l-4.5 4.75a.5.5 0 0 1-.733-.008l-2.5-2.75a.5.5 0 0 1 .74-.672l2.138 2.351 4.129-4.359a.5.5 0 0 1 .707-.019"
clip-rule="evenodd"
/>
<path
fill="#288419"
fill-rule="evenodd"
d="M8 1a7 7 0 1 0 0 14A7 7 0 0 0 8 1M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8"
clip-rule="evenodd"
/>
</svg>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,18 @@
font-size: calc(11 * var(--px-in-rem));
line-height: calc(14 * var(--px-in-rem));
color: var(--ntp-color-primary);
text-wrap: wrap; /* needed for some languages */

/* the active state created an awkward flash, adding transition out */
&.supressActiveStateForSwitchToConfirmationText {
opacity: 1;
transition: opacity .3s ease-out;

&:active {
background-color: var(--color-black-at-6);
opacity: 0;
}
}

&:hover {
background-color: var(--color-black-at-6);
Expand All @@ -73,6 +85,12 @@
}

@media screen and (prefers-color-scheme: dark) {
&.supressActiveStateForSwitchToConfirmationText {
&:active {
background-color: var(--color-black-at-9);
}
}

&:hover:not(:active) {
background-color: var(--color-black-at-9);
}
Expand All @@ -89,6 +107,26 @@
}
}

.confirmation {
display: flex;
align-items: center;
transition: all .2s ease-in;
min-height: 26px;

svg {
height: 1rem;
width: 1rem;
margin-right: var(--sp-2);
}

p {
font-size: calc(11 * var(--px-in-rem));
shakyShane marked this conversation as resolved.
Show resolved Hide resolved
line-height: calc(14 * var(--px-in-rem));
font-weight: 600;
max-width: 8rem;
}
}

.dismissBtn {
position: absolute;
top: 0.5rem;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { h } from 'preact';
import styles from './NextSteps.module.css';
import cn from 'classnames';

import { useState } from 'preact/hooks';
import { DismissButton } from '../../components/DismissButton';
import { variants } from '../nextsteps.data';
import { CheckColor } from '../../components/Icons';
import { useTypedTranslationWith } from '../../types';
import { variants, additionalCardStates } from '../nextsteps.data';
import styles from './NextSteps.module.css';

/**
* @param {object} props
Expand All @@ -14,14 +18,35 @@ import { useTypedTranslationWith } from '../../types';
export function NextStepsCard({ type, dismiss, action }) {
const { t } = useTypedTranslationWith(/** @type {import("../strings.json")} */ ({}));
const message = variants[type]?.(t);
const [showConfirmation, setShowConfirmation] = useState(false);
const hasConfirmationState = additionalCardStates.hasConfirmationText(message.id);

const handleClick = () => {
if (!hasConfirmationState) {
return action(message.id);
}

action(message.id);
setShowConfirmation(true);
};
return (
<div class={styles.card}>
<img src={`./icons/${message.icon}-128.svg`} alt="" class={styles.icon} />
<h3 class={styles.title}>{message.title}</h3>
<p class={styles.description}>{message.summary}</p>
<button class={styles.btn} onClick={() => action(message.id)}>
{message.actionText}
</button>
{hasConfirmationState && !!showConfirmation ? (
<div class={styles.confirmation}>
<CheckColor />
<p>{message.confirmationText}</p>
</div>
) : (
<button
class={cn(styles.btn, hasConfirmationState && styles.supressActiveStateForSwitchToConfirmationText)}
onClick={handleClick}
>
{message.actionText}
</button>
)}

<DismissButton className={styles.dismissBtn} onClick={() => dismiss(message.id)} />
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { h } from 'preact';
import cn from 'classnames';
import styles from './NextSteps.module.css';
import { useId } from 'preact/hooks';
import { ShowHideButton } from '../../components/ShowHideButton';
import { useTypedTranslationWith } from '../../types';
import { NextStepsCard } from './NextStepsCard';
import { otherText } from '../nextsteps.data';
import { ShowHideButton } from '../../components/ShowHideButton';
import { useId } from 'preact/hooks';
import styles from './NextSteps.module.css';
import { NextStepsCard } from './NextStepsCard';

/**
* @import enStrings from '../strings.json';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,14 @@ test.describe('newtab NextSteps cards', () => {
await page.getByRole('button', { name: 'Try DuckPlayer' }).click();
await ntp.mocks.waitForCallCount({ method: 'nextSteps_action', count: 1 });
});

test('shows a confirmation state', async ({ page }, workerInfo) => {
const ntp = NewtabPage.create(page, workerInfo);
await ntp.reducedMotion();
await ntp.openPage({ nextSteps: ['addAppToDockMac', 'defaultApp'] });
await page.getByRole('button', { name: 'Add to Dock' }).click();

await expect(page.getByText('Added to Dock!')).toBeVisible();
await expect(page.getByRole('button', { name: 'Add to Dock' })).not.toBeVisible();
});
shakyShane marked this conversation as resolved.
Show resolved Hide resolved
});
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export const variants = {
title: t('nextSteps_addAppDockMac_title'),
summary: t('nextSteps_addAppDockMac_summary'),
actionText: t('nextSteps_addAppDockMac_actionText'),
confirmationText: t('nextSteps_addAppDockMac_confirmationText'),
}),
/** @param {(translationId: keyof enStrings) => string} t */
pinAppToTaskbarWindows: (t) => ({
Expand All @@ -69,3 +70,10 @@ export const otherText = {
/** @param {(translationId: keyof enStrings) => string} t */
nextSteps_sectionTitle: (t) => t('nextSteps_sectionTitle'),
};

/** @type {string[]} cardsWithConfirmationText */
const cardsWithConfirmationText = ['addAppToDockMac'];

export const additionalCardStates = {
hasConfirmationText: (/** @type {keyof variants} */ variantId) => cardsWithConfirmationText.includes(variantId),
};
Loading