From be17da7305215f5e28e9ec109f746739ddd3ac34 Mon Sep 17 00:00:00 2001 From: Camden Cheek Date: Thu, 1 Aug 2024 03:06:37 -0600 Subject: [PATCH] Svelte: add welcome introduction when enabling svelte for the first time (#64163) This implements a welcome dialog and some additional messaging around the beta rollout. --- .../settings/temporary/TemporarySettings.ts | 5 + client/web-sveltekit/src/auto-imports.d.ts | 4 + .../navigation/FeedbackDialog.stories.svelte | 13 + .../src/lib/navigation/FeedbackDialog.svelte | 67 + .../src/lib/navigation/GlobalHeader.svelte | 71 +- .../src/lib/wildcard/Button.svelte | 1 + .../web-sveltekit/src/routes/+layout.svelte | 19 +- .../src/routes/WelcomeOverlay.svelte | 211 ++ .../WelcomeOverlayScreenshotDark.svelte | 826 ++++++ .../WelcomeOverlayScreenshotLight.svelte | 2550 +++++++++++++++++ .../web-sveltekit/src/routes/layout.spec.ts | 8 - .../sveltekit/SvelteKitNavItem.module.scss | 42 +- client/web/src/sveltekit/SvelteKitNavItem.tsx | 103 +- 13 files changed, 3837 insertions(+), 83 deletions(-) create mode 100644 client/web-sveltekit/src/lib/navigation/FeedbackDialog.stories.svelte create mode 100644 client/web-sveltekit/src/lib/navigation/FeedbackDialog.svelte create mode 100644 client/web-sveltekit/src/routes/WelcomeOverlay.svelte create mode 100644 client/web-sveltekit/src/routes/WelcomeOverlayScreenshotDark.svelte create mode 100644 client/web-sveltekit/src/routes/WelcomeOverlayScreenshotLight.svelte diff --git a/client/shared/src/settings/temporary/TemporarySettings.ts b/client/shared/src/settings/temporary/TemporarySettings.ts index fed061a2866..5b1bb291f59 100644 --- a/client/shared/src/settings/temporary/TemporarySettings.ts +++ b/client/shared/src/settings/temporary/TemporarySettings.ts @@ -96,6 +96,9 @@ export interface TemporarySettingsSchema { /** OpenCodeGraph */ 'openCodeGraph.annotations.visible': boolean + + 'webNext.welcomeOverlay.dismissed': boolean + 'webNext.departureMessage.dismissed': boolean } /** @@ -161,6 +164,8 @@ const TEMPORARY_SETTINGS: Record = { 'simple.search.toggle': null, 'cody.onboarding.completed': null, 'openCodeGraph.annotations.visible': null, + 'webNext.welcomeOverlay.dismissed': null, + 'webNext.departureMessage.dismissed': null, } export const TEMPORARY_SETTINGS_KEYS = Object.keys(TEMPORARY_SETTINGS) as readonly (keyof TemporarySettings)[] diff --git a/client/web-sveltekit/src/auto-imports.d.ts b/client/web-sveltekit/src/auto-imports.d.ts index d34784afa1a..3fdb1fff588 100644 --- a/client/web-sveltekit/src/auto-imports.d.ts +++ b/client/web-sveltekit/src/auto-imports.d.ts @@ -42,6 +42,7 @@ declare global { const ILucideEye: typeof import('~icons/lucide/eye')['default'] const ILucideFile: typeof import('~icons/lucide/file')['default'] const ILucideFileCode: typeof import('~icons/lucide/file-code')['default'] + const ILucideFileDiff: typeof import('~icons/lucide/file-diff')['default'] const ILucideFileJson: typeof import('~icons/lucide/file-json')['default'] const ILucideFileSearch2: typeof import('~icons/lucide/file-search2')['default'] const ILucideFileStack: typeof import('~icons/lucide/file-stack')['default'] @@ -61,12 +62,14 @@ declare global { const ILucideGitCompareArrows: typeof import('~icons/lucide/git-compare-arrows')['default'] const ILucideGitFork: typeof import('~icons/lucide/git-fork')['default'] const ILucideGitMerge: typeof import('~icons/lucide/git-merge')['default'] + const ILucideHelp: typeof import('~icons/lucide/help')['default'] const ILucideHistory: typeof import('~icons/lucide/history')['default'] const ILucideHome: typeof import('~icons/lucide/home')['default'] const ILucideInfo: typeof import('~icons/lucide/info')['default'] const ILucideLink: typeof import('~icons/lucide/link')['default'] const ILucideLock: typeof import('~icons/lucide/lock')['default'] const ILucideMenu: typeof import('~icons/lucide/menu')['default'] + const ILucideNetwork: typeof import('~icons/lucide/network')['default'] const ILucideOctagonX: typeof import('~icons/lucide/octagon-x')['default'] const ILucidePanelBottomClose: typeof import('~icons/lucide/panel-bottom-close')['default'] const ILucidePanelLeftClose: typeof import('~icons/lucide/panel-left-close')['default'] @@ -74,6 +77,7 @@ declare global { const ILucidePencil: typeof import('~icons/lucide/pencil')['default'] const ILucideRegex: typeof import('~icons/lucide/regex')['default'] const ILucideRepeat: typeof import('~icons/lucide/repeat')['default'] + const ILucideScanSearch: typeof import('~icons/lucide/scan-search')['default'] const ILucideSearch: typeof import('~icons/lucide/search')['default'] const ILucideSearchX: typeof import('~icons/lucide/search-x')['default'] const ILucideSettings: typeof import('~icons/lucide/settings')['default'] diff --git a/client/web-sveltekit/src/lib/navigation/FeedbackDialog.stories.svelte b/client/web-sveltekit/src/lib/navigation/FeedbackDialog.stories.svelte new file mode 100644 index 00000000000..138ebf427b0 --- /dev/null +++ b/client/web-sveltekit/src/lib/navigation/FeedbackDialog.stories.svelte @@ -0,0 +1,13 @@ + + + + {}} /> + diff --git a/client/web-sveltekit/src/lib/navigation/FeedbackDialog.svelte b/client/web-sveltekit/src/lib/navigation/FeedbackDialog.svelte new file mode 100644 index 00000000000..d18f46f5832 --- /dev/null +++ b/client/web-sveltekit/src/lib/navigation/FeedbackDialog.svelte @@ -0,0 +1,67 @@ + + +
+
+

New, faster UX (Beta)

+ {#if handleOptOut} + handleOptOut()} /> + {/if} +
+
+

+ You're currently on the new, faster Code Search user experience. It's in beta, and is our effort to rebuild + the tool from the ground up for performance. +

+
+
+

Got feedback for us on the beta? We'd love to hear from you.

+ + Leave feedback + +

It only takes two minutes and helps a ton!

+
+
+ + diff --git a/client/web-sveltekit/src/lib/navigation/GlobalHeader.svelte b/client/web-sveltekit/src/lib/navigation/GlobalHeader.svelte index 632160400f4..692bc866175 100644 --- a/client/web-sveltekit/src/lib/navigation/GlobalHeader.svelte +++ b/client/web-sveltekit/src/lib/navigation/GlobalHeader.svelte @@ -21,9 +21,10 @@ import SourcegraphLogo from '$lib/SourcegraphLogo.svelte' import { isViewportMediumDown } from '$lib/stores' import { Button } from '$lib/wildcard' - import Badge from '$lib/wildcard/Badge.svelte' - import Toggle from '$lib/wildcard/Toggle.svelte' + import { getButtonClassName } from '$lib/wildcard/Button' + import ProductStatusBadge from '$lib/wildcard/ProductStatusBadge.svelte' + import FeedbackDialog from './FeedbackDialog.svelte' import { GlobalNavigation_User } from './GlobalNavigation.gql' import { type NavigationEntry, type NavigationMenu, isNavigationMenu, isCurrent } from './mainNavigation' import UserMenu from './UserMenu.svelte' @@ -123,28 +124,24 @@
- {#if handleOptOut} - handleOptOut && handleOptOut()} /> - {/if} - - -
-

Experimental web app

-

- You are using an experimental version of the Sourcegraph web app. This version is under active - development and may contain bugs or incomplete features. -

-

- If you encounter any issues, please report them in our community forums. -

- {#if handleOptOut} -

You can opt out of the new experience with the toggle above.

- {/if} -
+
@@ -413,35 +410,13 @@ } } - // Opt out experiment badge and tooltip styles - .opt-out { - all: unset; - cursor: pointer; - color: var(--link-color); - text-decoration: underline; - } - .web-next-notice { display: flex; align-items: center; gap: 0.5rem; - } - - .web-next-badge { - all: unset; - display: flex; - align-items: center; - cursor: pointer; - margin-left: auto; - } - - .web-next-content { - padding: 1rem; - width: 20rem; - - p:last-child { - margin-bottom: 0; - } + font-size: var(--font-size-small); + font-weight: 500; + margin-right: 1rem; } // Custom menu with sidebar navigation controls styles diff --git a/client/web-sveltekit/src/lib/wildcard/Button.svelte b/client/web-sveltekit/src/lib/wildcard/Button.svelte index 2aa4aba67e5..cfc201085aa 100644 --- a/client/web-sveltekit/src/lib/wildcard/Button.svelte +++ b/client/web-sveltekit/src/lib/wildcard/Button.svelte @@ -2,6 +2,7 @@ // In addition to the props explicitly listed here, this component also // accepts any HTMLButton attributes. Note that those will only be used when // the default implementation is used. + import type { HTMLButtonAttributes } from 'svelte/elements' import { type BUTTON_DISPLAY, type BUTTON_SIZES, type BUTTON_VARIANTS, getButtonClassName } from './Button' diff --git a/client/web-sveltekit/src/routes/+layout.svelte b/client/web-sveltekit/src/routes/+layout.svelte index 31104875056..da838aedfba 100644 --- a/client/web-sveltekit/src/routes/+layout.svelte +++ b/client/web-sveltekit/src/routes/+layout.svelte @@ -22,6 +22,7 @@ import { isRouteEnabled } from '$lib/navigation' import type { LayoutData } from './$types' + import WelcomeOverlay from './WelcomeOverlay.svelte' export let data: LayoutData @@ -81,12 +82,17 @@ $: currentUserID = data.user?.id $: handleOptOut = currentUserID ? async (): Promise => { - if (currentUserID) { - await data.disableSvelteFeatureFlags(currentUserID) - window.location.reload() - } + // Show departure message after switching off + $temporarySettingsStorage.set('webNext.departureMessage.dismissed', false) + await data.disableSvelteFeatureFlags(currentUserID) + window.location.reload() } : undefined + + $: welcomeOverlayDismissed = $temporarySettingsStorage.get('webNext.welcomeOverlay.dismissed', false) + function handleDismissWelcomeOverlay() { + $temporarySettingsStorage.set('webNext.welcomeOverlay.dismissed', true) + } @@ -105,6 +111,11 @@ + + diff --git a/client/web-sveltekit/src/routes/WelcomeOverlayScreenshotDark.svelte b/client/web-sveltekit/src/routes/WelcomeOverlayScreenshotDark.svelte new file mode 100644 index 00000000000..a4a2d9cbc2a --- /dev/null +++ b/client/web-sveltekit/src/routes/WelcomeOverlayScreenshotDark.svelte @@ -0,0 +1,826 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/client/web-sveltekit/src/routes/WelcomeOverlayScreenshotLight.svelte b/client/web-sveltekit/src/routes/WelcomeOverlayScreenshotLight.svelte new file mode 100644 index 00000000000..b5dd5a0516b --- /dev/null +++ b/client/web-sveltekit/src/routes/WelcomeOverlayScreenshotLight.svelte @@ -0,0 +1,2550 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/client/web-sveltekit/src/routes/layout.spec.ts b/client/web-sveltekit/src/routes/layout.spec.ts index dfbe9366189..3862fdf8ef2 100644 --- a/client/web-sveltekit/src/routes/layout.spec.ts +++ b/client/web-sveltekit/src/routes/layout.spec.ts @@ -10,14 +10,6 @@ test('has sign in button', async ({ page }) => { await expect(page).toHaveURL('/sign-in') }) -test('has experimental opt out popover', async ({ sg, page }) => { - sg.signIn({ username: 'test' }) - - await page.goto('/') - await page.getByText('Experimental').click() - await expect(page.getByText('opt out')).toBeVisible() -}) - test('has user menu', async ({ sg, page }) => { sg.signIn({ username: 'test' }) const userMenu = page.getByLabel('Open user menu') diff --git a/client/web/src/sveltekit/SvelteKitNavItem.module.scss b/client/web/src/sveltekit/SvelteKitNavItem.module.scss index 778c1ee477b..9587eb4caf0 100644 --- a/client/web/src/sveltekit/SvelteKitNavItem.module.scss +++ b/client/web/src/sveltekit/SvelteKitNavItem.module.scss @@ -2,6 +2,11 @@ display: flex; gap: 0.5rem; align-items: center; + font-size: 0.875rem; + + p { + margin: 0; + } } .toggle { @@ -13,10 +18,39 @@ } .popover { - padding: 1rem; - width: 20rem; + width: 24rem; - p:last-child { - margin-bottom: 0; + p { + margin: 0; + } + + a { + width: 100%; + } + + h3 { + display: flex; + align-items: center; + justify-content: space-between; } } + +.section { + padding: 1rem 1.25rem; + display: flex; + flex-direction: column; + gap: 0.5rem; + + .section { + border-top: 1px solid var(--border-color); + } +} + +.small { + text-align: center; + font-size: var(--font-size-small); + color: var(--text-muted); +} + +.help-icon { + color: var(--icon-color); +} diff --git a/client/web/src/sveltekit/SvelteKitNavItem.tsx b/client/web/src/sveltekit/SvelteKitNavItem.tsx index d43c111d948..669c936b8cb 100644 --- a/client/web/src/sveltekit/SvelteKitNavItem.tsx +++ b/client/web/src/sveltekit/SvelteKitNavItem.tsx @@ -1,10 +1,12 @@ -import { FC } from 'react' +import { FC, useRef, useEffect, useCallback } from 'react' import { useApolloClient } from '@apollo/client' +import { mdiHelpCircleOutline, mdiClose } from '@mdi/js' import { useLocation } from 'react-router-dom' import { Toggle } from '@sourcegraph/branded/src/components/Toggle' -import { Text, H3, Popover, PopoverTrigger, PopoverContent, Badge } from '@sourcegraph/wildcard' +import { useTemporarySetting } from '@sourcegraph/shared/src/settings/temporary' +import { Text, H3, Popover, PopoverTrigger, PopoverContent, Icon, Button } from '@sourcegraph/wildcard' import { enableSvelteAndReload, canEnableSvelteKit } from './util' @@ -13,28 +15,91 @@ import styles from './SvelteKitNavItem.module.scss' export const SvelteKitNavItem: FC<{ userID?: string }> = ({ userID }) => { const location = useLocation() const client = useApolloClient() + const [departureDismissed, setDepartureDismissed] = useTemporarySetting('webNext.departureMessage.dismissed', false) + const [_welcomeDismissed, setWelcomeDismissed] = useTemporarySetting('webNext.welcomeOverlay.dismissed', false) + + const departureRef = useRef(null) + + const handleClickOutside = useCallback( + (event: MouseEvent) => { + if (departureRef.current && !departureRef.current.contains(event.target as Node)) { + setDepartureDismissed(true) + } + }, + [departureRef, setDepartureDismissed] + ) + + useEffect(() => { + document.addEventListener('click', handleClickOutside) + return () => { + document.removeEventListener('click', handleClickOutside) + } + }, [handleClickOutside]) if (!userID || !canEnableSvelteKit(location.pathname)) { return null } + const showDeparture = !departureDismissed + const popoverProps = showDeparture ? { isOpen: true, onOpenChange: () => {} } : {} + return ( -
- enableSvelteAndReload(client, userID)} - title="Go to experimental web app" - className={styles.toggle} - /> - - - Try the new experience - - -

Sourcegraph is getting a refresh!

- Try it out early with the toggle above. -
-
-
+ + +
+ + New, faster UX + { + setWelcomeDismissed(false) // Show welcome after switching on + enableSvelteAndReload(client, userID) + }} + title="Enable new, faster UX" + className={styles.toggle} + /> +
+
+ + {showDeparture ? ( +
+
+

+ Switched out of the new experience? + +

+ + Remember, you can always switch back using the toggle above. We're still working on it, + so check back soon. + +
+
+ Got feedback for us on the beta? We’d love to hear from you. + + It only takes two minutes and helps a ton! +
+
+ ) : ( +
+

What's this "New, faster UX"?

+ + We've been busy at work on a new Code Search experience, built from the ground up for + performance, which is now available in beta. + + Simply activate the toggle to get it. +
+ )} +
+
) }