UX: Improve user tips (#22518)

- Add an icon to the bootstrap user tip to draw attention
- Remove the second "don't show user tip" button from every user tip
This commit is contained in:
Bianca Nenciu 2023-07-11 18:22:40 +03:00 committed by GitHub
parent 5a30583174
commit bdb9ee8507
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 26 additions and 62 deletions

View File

@ -7,7 +7,8 @@
{{#if this.showUserTip}} {{#if this.showUserTip}}
<UserTip <UserTip
@id="admin_guide" @id="admin_guide"
@primaryLabel="user_tips.admin_guide.primary" @buttonLabel="user_tips.admin_guide.button"
@buttonIcon="link"
@onDismiss={{this.routeToAdminGuide}} @onDismiss={{this.routeToAdminGuide}}
/> />
{{else}} {{else}}

View File

@ -20,9 +20,9 @@ export default class UserTip extends Component {
selector, selector,
content, content,
placement, placement,
primaryLabel, buttonLabel,
buttonIcon,
onDismiss, onDismiss,
onDismissAll,
} = this.args; } = this.args;
element = element.parentElement; element = element.parentElement;
@ -30,14 +30,14 @@ export default class UserTip extends Component {
id, id,
titleText: I18n.t(`user_tips.${id}.title`), titleText: I18n.t(`user_tips.${id}.title`),
contentText: content || I18n.t(`user_tips.${id}.content`), contentText: content || I18n.t(`user_tips.${id}.content`),
primaryText: primaryLabel ? I18n.t(primaryLabel) : null, buttonLabel,
buttonIcon,
reference: reference:
(selector && element.parentElement.querySelector(selector)) || (selector && element.parentElement.querySelector(selector)) ||
element, element,
appendTo: element.parentElement, appendTo: element.parentElement,
placement, placement,
onDismiss, onDismiss,
onDismissAll,
}); });
}); });
} }

View File

@ -67,6 +67,11 @@ export function showUserTip(options) {
return; return;
} }
let buttonText = escape(I18n.t(options.buttonLabel || "user_tips.button"));
if (options.buttonIcon) {
buttonText = `${iconHTML(options.buttonIcon)} ${buttonText}`;
}
instancesMap[options.id] = tippy(options.reference, { instancesMap[options.id] = tippy(options.reference, {
hideOnClick: false, hideOnClick: false,
trigger: "manual", trigger: "manual",
@ -87,12 +92,7 @@ export function showUserTip(options) {
<div class='user-tip__title'>${escape(options.titleText)}</div> <div class='user-tip__title'>${escape(options.titleText)}</div>
<div class='user-tip__content'>${escape(options.contentText)}</div> <div class='user-tip__content'>${escape(options.contentText)}</div>
<div class='user-tip__buttons'> <div class='user-tip__buttons'>
<button class="btn btn-primary btn-dismiss">${escape( <button class="btn btn-primary">${buttonText}</button>
options.primaryText || I18n.t("user_tips.primary")
)}</button>
<button class="btn btn-flat btn-text btn-dismiss-all">${escape(
options.secondaryBtnText || I18n.t("user_tips.secondary")
)}</button>
</div> </div>
</div>`, </div>`,
@ -101,18 +101,11 @@ export function showUserTip(options) {
tippyInstance.popper.classList.add("user-tip"); tippyInstance.popper.classList.add("user-tip");
tippyInstance.popper tippyInstance.popper
.querySelector(".btn-dismiss") .querySelector(".btn")
.addEventListener("click", (event) => { .addEventListener("click", (event) => {
options.onDismiss?.(); options.onDismiss?.();
event.preventDefault(); event.preventDefault();
}); });
tippyInstance.popper
.querySelector(".btn-dismiss-all")
.addEventListener("click", (event) => {
options.onDismissAll?.();
event.preventDefault();
});
}, },
}); });

View File

@ -37,7 +37,6 @@ import { cancel } from "@ember/runloop";
import discourseLater from "discourse-common/lib/later"; import discourseLater from "discourse-common/lib/later";
import { isTesting } from "discourse-common/config/environment"; import { isTesting } from "discourse-common/config/environment";
import { import {
hideAllUserTips,
hideUserTip, hideUserTip,
showNextUserTip, showNextUserTip,
showUserTip, showUserTip,
@ -1202,10 +1201,6 @@ const User = RestModel.extend({
options.onDismiss?.(); options.onDismiss?.();
this.hideUserTipForever(options.id); this.hideUserTipForever(options.id);
}, },
onDismissAll: () => {
options.onDismissAll?.();
this.hideUserTipForever();
},
}); });
} }
}, },
@ -1217,45 +1212,29 @@ const User = RestModel.extend({
} }
// Empty userTipId means all user tips. // Empty userTipId means all user tips.
if (userTipId && !userTips[userTipId]) { if (!userTips[userTipId]) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.warn("Cannot hide user tip with type =", userTipId); console.warn("Cannot hide user tip with type =", userTipId);
return; return;
} }
// Hide user tips and maybe show the next one. // Hide user tips and maybe show the next one.
if (userTipId) {
hideUserTip(userTipId, true); hideUserTip(userTipId, true);
showNextUserTip(); showNextUserTip();
} else {
hideAllUserTips();
}
// Update list of seen user tips. // Update list of seen user tips.
let seenUserTips = this.user_option?.seen_popups || []; let seenUserTips = this.user_option?.seen_popups || [];
if (userTipId) {
if (seenUserTips.includes(userTips[userTipId])) { if (seenUserTips.includes(userTips[userTipId])) {
return; return;
} }
seenUserTips.push(userTips[userTipId]); seenUserTips.push(userTips[userTipId]);
} else {
if (seenUserTips.includes(-1)) {
return;
}
seenUserTips = [-1];
}
// Save seen user tips on the server. // Save seen user tips on the server.
if (!this.user_option) { if (!this.user_option) {
this.set("user_option", {}); this.set("user_option", {});
} }
this.set("user_option.seen_popups", seenUserTips); this.set("user_option.seen_popups", seenUserTips);
if (userTipId) {
return this.save(["seen_popups"]); return this.save(["seen_popups"]);
} else {
this.set("user_option.skip_new_user_tips", true);
return this.save(["seen_popups", "skip_new_user_tips"]);
}
}, },
}); });

View File

@ -235,16 +235,4 @@ module("Unit | Model | user", function (hooks) {
assert.ok(hideSpy.calledWith("first_notification")); assert.ok(hideSpy.calledWith("first_notification"));
assert.ok(showNextSpy.calledWith()); assert.ok(showNextSpy.calledWith());
}); });
test("hideUserTipForever() can hide all the user tips", async function (assert) {
const site = getOwner(this).lookup("service:site");
site.set("user_tips", { first_notification: 1 });
const store = getOwner(this).lookup("service:store");
const user = store.createRecord("user", { username: "eviltrout" });
const hideAllSpy = sinon.spy(userTips, "hideAllUserTips");
await user.hideUserTipForever();
assert.ok(hideAllSpy.calledWith());
});
}); });

View File

@ -18,6 +18,10 @@
&:hover { &:hover {
color: var(--tertiary-hover); color: var(--tertiary-hover);
} }
.d-icon {
color: var(--tertiary);
}
} }
> .tippy-svg-arrow { > .tippy-svg-arrow {

View File

@ -1909,8 +1909,7 @@ en:
remove_status: "Remove status" remove_status: "Remove status"
user_tips: user_tips:
primary: "Got it!" button: "Got it!"
secondary: "don't show me these tips"
first_notification: first_notification:
title: "Your first notification!" title: "Your first notification!"
@ -1935,7 +1934,7 @@ en:
admin_guide: admin_guide:
title: "Welcome to your new site!" title: "Welcome to your new site!"
content: "Read the admin guide to continue building your site and community." content: "Read the admin guide to continue building your site and community."
primary: "Let's go!" button: "Let's go!"
loading: "Loading..." loading: "Loading..."
errors: errors: