Skip to content
Draft
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
3 changes: 2 additions & 1 deletion special-pages/pages/new-tab/app/components/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ import styles from './App.module.css';
import { useCustomizerDrawerSettings, usePlatformName } from '../settings.provider.js';
import { WidgetList } from '../widget-list/WidgetList.js';
import { useGlobalDropzone } from '../dropzone.js';
import { CustomizerButton, CustomizerMenuPositionedFixed, useContextMenu } from '../customizer/components/CustomizerMenu.js';
import { CustomizerButton, CustomizerMenuPositionedFixed } from '../customizer/components/CustomizerMenu.js';
import { useDrawer, useDrawerControls } from './Drawer.js';
import { CustomizerDrawer } from '../customizer/components/CustomizerDrawer.js';
import { BackgroundConsumer } from './BackgroundProvider.js';
import { useComputed } from '@preact/signals';
import { CustomizerThemesContext } from '../customizer/CustomizerProvider.js';
import { useContext } from 'preact/hooks';
import { InlineErrorBoundary } from '../InlineErrorBoundary.js';
import { useContextMenu } from '../context-menu.js';

/**
* Renders the App component.
Expand Down
73 changes: 73 additions & 0 deletions special-pages/pages/new-tab/app/context-menu.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { useMessaging } from './types.js';
import { useEffect } from 'preact/hooks';

// eslint-disable-next-line @typescript-eslint/no-unused-vars
class ContextMenuItem {
/** @type {string} */
title;
/** @type {string} */
id;
/** @type {number} */
index;
}

const OPEN_EVENT = 'ntp-contextMenu-open';

/**
* @returns {ContextMenuItem[]}
*/
export function collect() {
/** @type {ContextMenuItem[]} */
const next = [];
const detail = {
register: (/** @type {ContextMenuItem} */ incoming) => {
next.push(incoming);
},
};
const event = new CustomEvent(OPEN_EVENT, { detail });
window.dispatchEvent(event);
next.sort((a, b) => a.index - b.index);
return next;
}

/**
* Forward the contextmenu event
*/
export function useContextMenu() {
const messaging = useMessaging();
useEffect(() => {
function handler(e) {
e.preventDefault();
e.stopImmediatePropagation();
const items = collect();
/** @type {Omit<ContextMenuItem, "index">[]} */
const simplified = items
.filter((x) => !x.id.startsWith('_'))
.map((item) => {
return {
id: item.id,
title: item.title,
};
});
messaging.contextMenu({ visibilityMenuItems: simplified });
}
document.body.addEventListener('contextmenu', handler);
return () => {
document.body.removeEventListener('contextmenu', handler);
};
}, [messaging]);
}

/**
* Call this to opt-in to the visibility menu
* @param {ContextMenuItem} row
*/
export function useContextMenuItem({ title, id, index }) {
useEffect(() => {
const handler = (/** @type {CustomEvent<any>} */ e) => {
e.detail.register({ title, id, index });
};
window.addEventListener(OPEN_EVENT, handler);
return () => window.removeEventListener(OPEN_EVENT, handler);
}, [title, id, index]);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { h } from 'preact';
import { useEffect } from 'preact/hooks';
import styles from './Customizer.module.css';
import { CustomizeIcon } from '../../components/Icons.js';
import { useMessaging, useTypedTranslation } from '../../types.js';
import { useTypedTranslation } from '../../types.js';

/**
* @import { WidgetVisibility, VisibilityMenuItem } from '../../../types/new-tab.js'
Expand All @@ -21,7 +21,10 @@ import { useMessaging, useTypedTranslation } from '../../types.js';
export const OPEN_EVENT = 'ntp-customizer-open';
export const UPDATE_EVENT = 'ntp-customizer-update';

export function getItems() {
/**
* @returns {VisibilityRowData[]}
*/
export function getCustomizerItems() {
/** @type {VisibilityRowData[]} */
const next = [];
const detail = {
Expand All @@ -35,34 +38,6 @@ export function getItems() {
return next;
}

/**
* Forward the contextmenu event
*/
export function useContextMenu() {
const messaging = useMessaging();
useEffect(() => {
function handler(e) {
e.preventDefault();
e.stopImmediatePropagation();
const items = getItems();
/** @type {VisibilityMenuItem[]} */
const simplified = items
.filter((x) => !x.id.startsWith('_'))
.map((item) => {
return {
id: item.id,
title: item.title,
};
});
messaging.contextMenu({ visibilityMenuItems: simplified });
}
document.body.addEventListener('contextmenu', handler);
return () => {
document.body.removeEventListener('contextmenu', handler);
};
}, [messaging]);
}

/**
* @param {object} props
* @param {string} [props.menuId]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { useLayoutEffect, useState } from 'preact/hooks';
import { getItems, UPDATE_EVENT } from './CustomizerMenu.js';
import { getCustomizerItems, UPDATE_EVENT } from './CustomizerMenu.js';
import { EmbeddedVisibilityMenu } from './VisibilityMenu.js';
import { h } from 'preact';

export function VisibilityMenuSection() {
const [rowData, setRowData] = useState(() => {
const items = /** @type {import("./CustomizerMenu.js").VisibilityRowData[]} */ (getItems());
const items = /** @type {import("./CustomizerMenu.js").VisibilityRowData[]} */ (getCustomizerItems());
return items;
});
useLayoutEffect(() => {
function handler() {
setRowData(getItems());
setRowData(getCustomizerItems());
}

window.addEventListener(UPDATE_EVENT, handler);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { FavoritesMemo } from './Favorites.js';
import { viewTransition } from '../../utils.js';
import { CustomizerContext } from '../../customizer/CustomizerProvider.js';
import { Shield } from '../../components/Icons.js';
import { useContextMenuItem } from '../../context-menu.js';

/**
* @typedef {import('../../../types/new-tab.ts').Favorite} Favorite
Expand Down Expand Up @@ -68,6 +69,7 @@ export function FavoritesCustomized() {
// register with the visibility menu
const title = t('favorites_menu_title');
useCustomizer({ title, id, icon: <Shield />, toggle, visibility: visibility.value, index });
useContextMenuItem({ title, id, index });

if (visibility.value === 'hidden') {
return null;
Expand Down
2 changes: 1 addition & 1 deletion special-pages/pages/new-tab/app/mock-transport.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { tabsMockTransport } from './tabs/tabs.mock-transport.js';
* @typedef {import('@duckduckgo/messaging/lib/test-utils.mjs').SubscriptionEvent} SubscriptionEvent
*/

const VERSION_PREFIX = '__ntp_31__.';
const VERSION_PREFIX = '__ntp_32__.';
const url = new URL(window.location.href);

export function mockTransport() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ function AiSetting({ enableAi, setEnableAi }) {
const { id, index } = useVisibility();
useCustomizer({
title: t('omnibar_toggleDuckAi'),
id: `_${id}-toggleAi`,
id: `${id}-toggleAi`,
icon: <ArrowIndentCenteredIcon style={{ color: 'var(--ntp-icons-tertiary)' }} />,
toggle: () => setEnableAi(!enableAi),
visibility: enableAi ? 'visible' : 'hidden',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { h } from 'preact';

import { OmnibarConsumer } from './OmnibarConsumer.js';
import { SearchIcon } from '../../components/Icons.js';
import { useContextMenuItem } from '../../context-menu.js';
import { PersistentModeProvider, PersistentTextInputProvider } from './PersistentOmnibarValuesProvider.js';

/**
Expand All @@ -30,6 +31,12 @@ export function OmnibarCustomized() {
const { visibility, id, toggle, index } = useVisibility();

useCustomizer({ title: sectionTitle, id, icon: <SearchIcon />, toggle, visibility: visibility.value, index });
useContextMenuItem({ title: sectionTitle, id, index });
useContextMenuItem({
title: t('omnibar_toggleDuckAi'),
id: `${id}-toggleAi`,
index: index + 0.1,
});

return (
<PersistentTextInputProvider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -993,10 +993,11 @@ test.describe('omnibar widget', () => {
// Right-click on the page to trigger context menu
await page.click('body', { button: 'right' });

// Assert that contextMenu notification is sent with real widgets and not e.g. the Duck.ai toggle
// Assert that contextMenu notification is sent with an additional widget.
await omnibar.expectMethodCalledWith('contextMenu', {
visibilityMenuItems: [
{ id: 'omnibar', title: 'Search' },
{ id: 'omnibar-toggleAi', title: 'Duck.ai' },
{ id: 'favorites', title: 'Favorites' },
{ id: 'protections', title: 'Protections Report' },
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { h } from 'preact';

import { ProtectionsConsumer } from './ProtectionsConsumer.js';
import { DuckFoot } from '../../components/Icons.js';
import { useContextMenuItem } from '../../context-menu.js';

/**
* @import enStrings from "../strings.json"
Expand All @@ -29,6 +30,7 @@ export function ProtectionsCustomized() {
const { visibility, id, toggle, index } = useVisibility();

useCustomizer({ title: sectionTitle, id, icon: <DuckFoot />, toggle, visibility: visibility.value, index });
useContextMenuItem({ title: sectionTitle, id, index });

if (visibility.value === 'hidden') {
return null;
Expand Down
Loading