Moved onboarding display check into new onboarding service (#19938)

refs
https://linear.app/tryghost/issue/IPC-92/add-logic-for-completing-steps

- added `onboarding` service to manage logic and state for the onboarding display and it's various steps
- added basic "display onboarding checklist" state to replicate the basic feature flag toggle along with making sure it's only shown to owners
- added acceptance test file and missing mirage endpoints needed for the dashboard to load without error
This commit is contained in:
Kevin Ansfield 2024-03-27 17:37:37 +00:00 committed by GitHub
parent 75b08a716b
commit 919ec733e7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 104 additions and 16 deletions

View File

@ -1,5 +1,5 @@
<div class="gh-onboarding-wrapper"> <div class="gh-onboarding-wrapper" data-test-dashboard="onboarding-checklist">
<div class="gh-onboarding-header"> <div class="gh-onboarding-header">
<video width="80" height="80" loop autoplay muted playsinline preload="metadata" style="width: 80px; height: 80px; margin-bottom: 24px;"> <video width="80" height="80" loop autoplay muted playsinline preload="metadata" style="width: 80px; height: 80px; margin-bottom: 24px;">
<source src="assets/videos/logo-loader.mp4" type="video/mp4" /> <source src="assets/videos/logo-loader.mp4" type="video/mp4" />
</video> </video>
@ -10,7 +10,7 @@
<div class="gh-onboarding-items"> <div class="gh-onboarding-items">
<div> <div>
{{!-- Step 1 --}} {{!-- Step 1 --}}
<div class="gh-onboarding-item gh-onboarding-item--completed"> <div class="gh-onboarding-item gh-onboarding-item--completed">
<div class="gh-onboarding-item-content"> <div class="gh-onboarding-item-content">
{{svg-jar "rocket" }} {{svg-jar "rocket" }}
<div class="gh-onboarding-item-details"> <div class="gh-onboarding-item-details">
@ -22,7 +22,7 @@
</div> </div>
</div> </div>
{{!-- Step 2 --}} {{!-- Step 2 --}}
<LinkTo @route="settings-x.settings-x" @model="design/edit?ref=setup" class="gh-onboarding-item gh-onboarding-item--next"> <LinkTo @route="settings-x.settings-x" @model="design/edit?ref=setup" class="gh-onboarding-item gh-onboarding-item--next">
<div class="gh-onboarding-item-content"> <div class="gh-onboarding-item-content">
{{svg-jar "brush" }} {{svg-jar "brush" }}
<div class="gh-onboarding-item-details"> <div class="gh-onboarding-item-details">
@ -35,7 +35,7 @@
</div> </div>
</LinkTo> </LinkTo>
{{!-- Step 3 --}} {{!-- Step 3 --}}
<LinkTo @route="lexical-editor.new" @model="post" class="gh-onboarding-item"> <LinkTo @route="lexical-editor.new" @model="post" class="gh-onboarding-item">
<div class="gh-onboarding-item-content"> <div class="gh-onboarding-item-content">
{{svg-jar "writing" }} {{svg-jar "writing" }}
<div class="gh-onboarding-item-details"> <div class="gh-onboarding-item-details">
@ -61,7 +61,7 @@
</div> </div>
</LinkTo> </LinkTo>
{{!-- Step 5 --}} {{!-- Step 5 --}}
<div role="button" {{on "click" (toggle-action "showShareModal" this)}} class="gh-onboarding-item"> <div role="button" {{on "click" (toggle-action "showShareModal" this)}} class="gh-onboarding-item">
<div class="gh-onboarding-item-content"> <div class="gh-onboarding-item-content">
{{svg-jar "megaphone" }} {{svg-jar "megaphone" }}
<div class="gh-onboarding-item-details"> <div class="gh-onboarding-item-details">
@ -79,7 +79,7 @@
<a href="#" class="gh-onboarding-explore-dashboard">Explore your dashboard</a> <a href="#" class="gh-onboarding-explore-dashboard">Explore your dashboard</a>
<p class="gh-onboarding-help">Need some more help? Check out our <a href="https://ghost.org/help?utm_source=admin&utm_campaign=onboarding" target="_blank" rel="noopener noreferrer">Help center</a></p> <p class="gh-onboarding-help">Need some more help? Check out our <a href="https://ghost.org/help?utm_source=admin&utm_campaign=onboarding" target="_blank" rel="noopener noreferrer">Help center</a></p>
<a href="#" class="gh-onboarding-skip">Skip onboarding</a> <a href="#" class="gh-onboarding-skip">Skip onboarding</a>
</div> </div>

View File

@ -18,10 +18,11 @@ const DAYS_OPTIONS = [{
export default class DashboardController extends Controller { export default class DashboardController extends Controller {
@service dashboardStats; @service dashboardStats;
@service membersUtils;
@service store;
@service mentionUtils;
@service feature; @service feature;
@service membersUtils;
@service mentionUtils;
@service onboarding;
@service store;
@tracked mentions = []; @tracked mentions = [];
@tracked hasNewMentions = false; @tracked hasNewMentions = false;

View File

@ -1,7 +1,7 @@
import Helper from '@ember/component/helper'; import Helper from '@ember/component/helper';
import {inject as service} from '@ember/service'; import {inject as service} from '@ember/service';
export default class EnableDeveloperExperimentsHelper extends Helper { export default class FeatureHelper extends Helper {
@service feature; @service feature;
compute([featureFlag]) { compute([featureFlag]) {

View File

@ -0,0 +1,11 @@
import Service, {inject as service} from '@ember/service';
export default class OnboardingService extends Service {
@service feature;
@service session;
get isChecklistShown() {
return this.feature.onboardingChecklist
&& this.session.user.isOwnerOnly;
}
}

View File

@ -1,7 +1,7 @@
<section class="gh-canvas" {{scroll-top}}> <section class="gh-canvas" {{scroll-top}}>
<div class="gh-dashboard"> <div class="gh-dashboard">
{{#unless (feature 'onboardingChecklist')}} {{#unless this.onboarding.isChecklistShown}}
<GhCanvasHeader class="gh-canvas-header sticky"> <GhCanvasHeader class="gh-canvas-header sticky" data-test-dashboard="header">
<h2 class="gh-canvas-title" data-test-screen-title> <h2 class="gh-canvas-title" data-test-screen-title>
Dashboard Dashboard
</h2> </h2>
@ -80,13 +80,14 @@
{{/unless}} {{/unless}}
</GhCanvasHeader> </GhCanvasHeader>
{{/unless}} {{/unless}}
<section class="gh-dashboard-layout"> <section class="gh-dashboard-layout">
{{#if this.isLoading }} {{#if this.isLoading }}
<GhLoadingSpinner /> <GhLoadingSpinner />
{{else}} {{else}}
{{#if this.areMembersEnabled}} {{#if this.areMembersEnabled}}
{{#if (feature 'onboardingChecklist')}} {{#if this.onboarding.isChecklistShown}}
<Dashboard::OnboardingChecklist /> <Dashboard::OnboardingChecklist />
{{/if}} {{/if}}
@ -94,8 +95,8 @@
<Dashboard::Charts::Overview /> <Dashboard::Charts::Overview />
{{/if}} {{/if}}
{{#unless (feature 'onboardingChecklist')}} {{#unless this.onboarding.isChecklistShown}}
<div class="gh-dashboard-group {{if this.isTotalMembersZero 'is-zero'}}"> <div class="gh-dashboard-group {{if this.isTotalMembersZero 'is-zero'}}" data-test-dashboard="attribution">
<Dashboard::Charts::AnchorAttribution /> <Dashboard::Charts::AnchorAttribution />
{{#if this.hasPaidTiers}} {{#if this.hasPaidTiers}}
<section class="gh-dashboard-section"> <section class="gh-dashboard-section">

View File

@ -63,4 +63,11 @@ export default function mockStats(server) {
} }
}; };
}); });
server.get('/stats/referrers/', function () {
return {
stats: [],
meta: {}
};
});
} }

View File

@ -5,6 +5,12 @@ let themeCount = 1;
export default function mockThemes(server) { export default function mockThemes(server) {
server.get('/themes'); server.get('/themes');
server.get('/themes/active/', function ({themes}) {
const theme = themes.findBy({active: true});
return {themes: [theme]};
});
server.post('/themes/upload/', function ({themes}) { server.post('/themes/upload/', function ({themes}) {
// pretender/mirage doesn't currently process FormData so we can't use // pretender/mirage doesn't currently process FormData so we can't use
// any info passed in through the request // any info passed in through the request

View File

@ -0,0 +1,62 @@
import {authenticateSession} from 'ember-simple-auth/test-support';
import {currentURL, find, visit} from '@ember/test-helpers';
import {describe, it} from 'mocha';
import {enableLabsFlag} from '../helpers/labs-flag';
import {enableMembers} from '../helpers/members';
import {expect} from 'chai';
import {setupApplicationTest} from 'ember-mocha';
import {setupMirage} from 'ember-cli-mirage/test-support';
describe('Acceptance: Onboarding', function () {
const hooks = setupApplicationTest();
setupMirage(hooks);
beforeEach(async function () {
this.server.loadFixtures('configs');
this.server.loadFixtures('settings');
this.server.loadFixtures('themes');
enableLabsFlag(this.server, 'onboardingChecklist');
enableMembers(this.server);
});
describe('checklist (owner)', function () {
beforeEach(async function () {
let role = this.server.create('role', {name: 'Owner'});
this.server.create('user', {roles: [role], slug: 'owner'});
return await authenticateSession();
});
it('dashboard shows the checklist', async function () {
await visit('/dashboard');
expect(currentURL()).to.equal('/dashboard');
// main onboarding list is visible
expect(find('[data-test-dashboard="onboarding-checklist"]'), 'checklist').to.exist;
// other default dashboard elements get hidden
expect(find('[data-test-dashboard="header"]'), 'header').to.not.exist;
expect(find('[data-test-dashboard="attribution"]'), 'attribution section').to.not.exist;
});
});
describe('checklist (non-owner)', function () {
beforeEach(async function () {
let role = this.server.create('role', {name: 'Administrator'});
this.server.create('user', {roles: [role], slug: 'admin'});
return await authenticateSession();
});
it('dashboard doesn\'t show the checklist', async function () {
await visit('/dashboard');
expect(currentURL()).to.equal('/dashboard');
// onboarding isn't shown
expect(find('[data-test-dashboard="onboarding-checklist"]'), 'checklist').to.not.exist;
// other default dashboard elements are visible
expect(find('[data-test-dashboard="header"]'), 'header').to.exist;
expect(find('[data-test-dashboard="attribution"]'), 'attribution section').to.exist;
});
});
});