Skip to content

Commit

Permalink
Merge pull request #730 from MuckRock/allanlasser/issue598
Browse files Browse the repository at this point in the history
Guided tours for first-time users
  • Loading branch information
allanlasser authored Oct 4, 2024
2 parents 895bc50 + 270acb3 commit c4f5aae
Show file tree
Hide file tree
Showing 13 changed files with 288 additions and 24 deletions.
1 change: 1 addition & 0 deletions .storybook/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const preview: Preview = {
stores: {
page: {
url: "/",
route: { id: "/" },
data: {
breadcrumbs: [],
},
Expand Down
6 changes: 6 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"body-scroll-lock": "^2.6.4",
"core-js": "^3.37.1",
"dompurify": "^3.0.8",
"driver.js": "^1.3.1",
"fast-copy": "^2.1.0",
"fast-deep-equal": "^3.1.3",
"filesize": "^10.1.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,9 @@ exports[`Mailkey 1`] = `
class="svelte-65lbzd"
>
<button
class="primary normal svelte-141617s ghost"
class="primary normal ghost svelte-141617s"
title=""
type="button"
value="undefined"
>
<svg
class="octicon octicon-x-circle"
Expand Down Expand Up @@ -75,20 +74,18 @@ exports[`Mailkey 1`] = `
style="display: flex; flex-direction: row; flex-wrap: wrap; align-items: stretch; justify-content: center; gap: 1rem;"
>
<button
class="primary normal svelte-141617s minW"
class="primary normal minW svelte-141617s"
title=""
type="button"
value="undefined"
>
Create new email address
</button>
<button
class="danger normal svelte-141617s minW"
class="danger normal minW svelte-141617s"
title=""
type="button"
value="undefined"
>
Delete existing address
</button>
Expand All @@ -112,10 +109,9 @@ exports[`Mailkey 2`] = `
class="svelte-65lbzd"
>
<button
class="primary normal svelte-141617s ghost"
class="primary normal ghost svelte-141617s"
title=""
type="button"
value="undefined"
>
<svg
class="octicon octicon-x-circle"
Expand Down Expand Up @@ -180,20 +176,18 @@ exports[`Mailkey 2`] = `
style="display: flex; flex-direction: row; flex-wrap: wrap; align-items: stretch; justify-content: center; gap: 1rem;"
>
<button
class="primary normal svelte-141617s minW"
class="primary normal minW svelte-141617s"
title=""
type="button"
value="undefined"
>
Create new email address
</button>
<button
class="danger normal svelte-141617s minW"
class="danger normal minW svelte-141617s"
title=""
type="button"
value="undefined"
>
Delete existing address
</button>
Expand Down
2 changes: 2 additions & 0 deletions src/lib/components/common/Button.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
class:ghost
class:full
class:minW
{...$$restProps}
>
<slot>{label}</slot>
</a>
Expand All @@ -56,6 +57,7 @@
class:ghost
class:full
class:minW
{...$$restProps}
>
<slot>{label}</slot>
</button>
Expand Down
1 change: 1 addition & 0 deletions src/lib/components/layouts/Navigation.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
ghost
mode="primary"
on:click={() => (feedbackOpen = true)}
id="feedback"
>
{$_("common.feedback")}
</Button>
Expand Down
2 changes: 1 addition & 1 deletion src/lib/components/layouts/Sidebar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
<svelte:window bind:innerWidth={viewWidth} />

{#if isOpen}
<aside class="sidebarContainer {position}" transition:fly={flyOptions}>
<aside class="sidebarContainer {position}" {id} transition:fly={flyOptions}>
<header class:reverse={position === "left"}>
{#if $$slots.title}
<span class="title"><slot name="title" /></span>
Expand Down
46 changes: 36 additions & 10 deletions src/lib/components/navigation/HelpMenu.svelte
Original file line number Diff line number Diff line change
@@ -1,23 +1,39 @@
<script lang="ts">
import { _ } from "svelte-i18n";
import Dropdown from "@/common/Dropdown2.svelte";
import Menu from "@/common/Menu.svelte";
import { page } from "$app/stores";
import { _ } from "svelte-i18n";
import {
ChevronDown12,
ChevronUp12,
Code16,
CommentDiscussion16,
Gift16,
Mail16,
Milestone16,
Plug16,
Question24,
Search16,
} from "svelte-octicons";
import Dropdown, { closeDropdown } from "@/common/Dropdown2.svelte";
import Menu from "@/common/Menu.svelte";
import SidebarItem from "../sidebar/SidebarItem.svelte";
import Premium from "@/common/icons/Premium.svelte";
import { startTour, isTourAvailable } from "../onboarding/GuidedTour.svelte";
export let position = "bottom right";
function close() {
closeDropdown("help");
}
function onTourClick() {
close();
startTour();
}
$: showTour = isTourAvailable($page?.route?.id);
</script>

<!-- Help Menu -->
Expand All @@ -33,31 +49,41 @@
</div>
</SidebarItem>
<Menu>
<SidebarItem href="/help/faq/">
{#if showTour}
<SidebarItem hover on:click={onTourClick}>
<Milestone16 slot="start" />
Guided Tour
</SidebarItem>
{/if}
<SidebarItem href="/help/faq/" on:click={close}>
<CommentDiscussion16 slot="start" />
{$_("authSection.help.faq")}
</SidebarItem>
<SidebarItem href="/help/search/">
<SidebarItem href="/help/search/" on:click={close}>
<Search16 slot="start" />
{$_("authSection.help.searchDocs")}
</SidebarItem>
<SidebarItem href="/help/api/">
<SidebarItem href="/help/api/" on:click={close}>
<Code16 slot="start" />
{$_("authSection.help.apiDocs")}
</SidebarItem>
<SidebarItem href="/help/add-ons/">
<SidebarItem href="/help/add-ons/" on:click={close}>
<Plug16 slot="start" />
{$_("authSection.help.addOns")}
</SidebarItem>
<SidebarItem href="/help/premium/">
<SidebarItem href="/help/premium/" on:click={close}>
<Premium slot="start" />
{$_("authSection.help.premium")}
</SidebarItem>
<SidebarItem href="https://www.muckrock.com/donate/">
<SidebarItem href="https://www.muckrock.com/donate/" on:click={close}>
<Gift16 slot="start" />
{$_("authSection.help.donate")}
</SidebarItem>
<SidebarItem href="mailto:[email protected]" target="_blank">
<SidebarItem
href="mailto:[email protected]"
target="_blank"
on:click={close}
>
<Mail16 slot="start" />
{$_("authSection.help.emailUs")}
</SidebarItem>
Expand Down
124 changes: 124 additions & 0 deletions src/lib/components/onboarding/GuidedTour.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
<!-- @component
The GuidedTour will run on different routes, introducing that page's
UI to new users. We keep track of which tours the user has already
taken in a LocalStorage object. If a script exists for the current route,
and if the user hasn't already taken it, we'll prompt them to take the guided tour.
-->
<script lang="ts" context="module">
import type { Maybe } from "$lib/api/types";
import "driver.js/dist/driver.css";
import { page } from "$app/stores";
import { driver, type Driver, type DriveStep, type Config } from "driver.js";
import { get } from "svelte/store";
import { StorageManager } from "$lib/utils/storage";
import { scripts } from "./scripts";
const storage = new StorageManager("guided-tour");
let driverObj: Driver;
const driverConfig: Config = {
showProgress: true,
popoverClass: "dc-driver",
overlayColor: "rgba(92, 113, 124, 0.5)",
};
type Tours = Record<string, boolean>;
export function getRoute(): Maybe<string> {
return get(page)?.route?.id;
}
export function getScript(route?: string): Maybe<DriveStep[]> {
const currentRoute = route ?? getRoute();
return scripts[currentRoute];
}
export function getTourHistory(): Tours {
return storage.get<Tours, {}>("tours", {});
}
export function startTour(): void {
driverObj?.drive();
}
export function endTour(): void {
const route = getRoute();
const history = getTourHistory();
storage.set("tours", { ...history, [route]: false });
driverObj.destroy();
}
export function isTourAvailable(route?: string): boolean {
return Boolean(getScript(route));
}
</script>

<script lang="ts">
import { onMount } from "svelte";
onMount(() => {
// do we have a tour?
const steps = getScript();
if (steps) {
driverObj = driver({
...driverConfig,
steps,
onDestroyStarted: endTour,
});
// should we start the tour?
const currentRoute = getRoute();
const history = getTourHistory();
const offerTour = history[currentRoute] ?? true;
if (offerTour) {
startTour();
}
}
});
</script>

<slot />

<style>
:global(
.driver-popover,
.driver-popover-title,
.driver-popover-description,
.driver-popover-progress-text,
.driver-popover-footer button
) {
font-family: var(--font-sans);
}
:global(.driver-popover-title) {
font-weight: var(--font-semibold);
line-height: 1.2;
font-size: var(--font-lg);
color: var(--gray-5);
}
:global(.driver-popover-description) {
font-size: var(--font-sm);
line-height: 1.4;
color: var(--gray-5);
}
:global(.driver-popover-progress-text) {
color: var(--gray-4);
}
:global(.driver-popover-footer button) {
font-weight: var(--font-semibold);
}
:global(button.driver-popover-next-btn) {
border-color: var(--blue-4);
background-color: var(--blue-3);
color: var(--white);
text-shadow: none;
}
:global(button.driver-popover-next-btn:hover) {
background-color: var(--blue-4);
color: var(--white);
text-shadow: none;
}
</style>
Loading

0 comments on commit c4f5aae

Please sign in to comment.