UX: Add show more button to long post queued reviewables (#23075)

This commit is contained in:
Keegan George 2023-08-14 10:11:30 -07:00 committed by GitHub
parent ac4e854a04
commit 61571bee43
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 192 additions and 64 deletions

View File

@ -0,0 +1,64 @@
import Component from "@glimmer/component";
import { inject as service } from "@ember/service";
import didUpdate from "@ember/render-modifiers/modifiers/did-update";
import { action } from "@ember/object";
import { tracked } from "@glimmer/tracking";
import { loadOneboxes } from "discourse/lib/load-oneboxes";
import { cookAsync } from "discourse/lib/text";
import { resolveAllShortUrls } from "pretty-text/upload-short-url";
import { ajax } from "discourse/lib/ajax";
export default class CookText extends Component {
<template>
{{! template-lint-disable modifier-name-case }}
<div
...attributes
{{didUpdate this.buildOneboxes this.cooked}}
{{didUpdate this.resolveShortUrls this.cooked}}
{{didUpdate this.calculateOffsetHeight this.cooked}}
>
{{this.cooked}}
</div>
</template>
@service siteSettings;
@tracked cooked = null;
constructor(owner, args) {
super(owner, args);
this.loadCookedText();
}
async loadCookedText() {
const cooked = await cookAsync(this.args.rawText);
this.cooked = cooked;
}
@action
calculateOffsetHeight(element) {
if (!this.args.onOffsetHeightCalculated) {
return;
}
return this.args.onOffsetHeightCalculated(element?.offsetHeight);
}
@action
buildOneboxes(element) {
if (this.args.paintOneboxes && this.cooked !== null) {
loadOneboxes(
element,
ajax,
this.args.topicId,
this.args.categoryId,
this.siteSettings.max_oneboxes_per_post,
false // refresh
);
}
}
@action
resolveShortUrls(element) {
resolveAllShortUrls(ajax, this.siteSettings, element, this.args.opts);
}
}

View File

@ -1 +0,0 @@
{{this.cooked}}

View File

@ -1,38 +0,0 @@
import Component from "@ember/component";
import { ajax } from "discourse/lib/ajax";
import { cookAsync } from "discourse/lib/text";
import { loadOneboxes } from "discourse/lib/load-oneboxes";
import { resolveAllShortUrls } from "pretty-text/upload-short-url";
const CookText = Component.extend({
cooked: null,
didReceiveAttrs() {
this._super(...arguments);
cookAsync(this.rawText).then((cooked) => {
this.set("cooked", cooked);
});
},
didRender() {
this._super(...arguments);
if (this.paintOneboxes) {
loadOneboxes(
this.element,
ajax,
this.topicId,
this.categoryId,
this.siteSettings.max_oneboxes_per_post,
false // refresh
);
}
resolveAllShortUrls(ajax, this.siteSettings, this.element, this.opts);
},
});
CookText.reopenClass({ positionalParams: ["rawText"] });
export default CookText;

View File

@ -1,11 +1,11 @@
<ReviewableTopicLink @reviewable={{this.reviewable}} @tagName=""> <ReviewableTopicLink @reviewable={{@reviewable}} @tagName="">
<div class="title-text"> <div class="title-text">
{{d-icon "plus-square" title="review.new_topic"}} {{d-icon "plus-square" title="review.new_topic"}}
{{this.reviewable.payload.title}} {{@reviewable.payload.title}}
</div> </div>
{{category-badge this.reviewable.category}} {{category-badge @reviewable.category}}
<ReviewableTags @tags={{this.reviewable.payload.tags}} @tagName="" /> <ReviewableTags @tags={{@reviewable.payload.tags}} @tagName="" />
{{#if this.reviewable.payload.via_email}} {{#if @reviewable.payload.via_email}}
<a href {{on "click" this.showRawEmail}} class="show-raw-email"> <a href {{on "click" this.showRawEmail}} class="show-raw-email">
{{d-icon "envelope" title="post.via_email"}} {{d-icon "envelope" title="post.via_email"}}
</a> </a>
@ -13,27 +13,34 @@
</ReviewableTopicLink> </ReviewableTopicLink>
<div class="post-contents-wrapper"> <div class="post-contents-wrapper">
<ReviewableCreatedBy <ReviewableCreatedBy @user={{@reviewable.target_created_by}} @tagName="" />
@user={{this.reviewable.target_created_by}}
@tagName=""
/>
<div class="post-contents"> <div class="post-contents">
<ReviewablePostHeader <ReviewablePostHeader
@reviewable={{this.reviewable}} @reviewable={{@reviewable}}
@createdBy={{this.reviewable.target_created_by}} @createdBy={{@reviewable.target_created_by}}
@tagName="" @tagName=""
/> />
<CookText <CookText
@rawText={{this.reviewable.payload.raw}} class="post-body {{if this.isCollapsed 'is-collapsed'}}"
@class="post-body" @rawText={{@reviewable.payload.raw}}
@categoryId={{this.reviewable.category_id}} @categoryId={{@reviewable.category_id}}
@topicId={{this.reviewable.topic_id}} @topicId={{@reviewable.topic_id}}
@paintOneboxes={{true}} @paintOneboxes={{true}}
@opts={{hash removeMissing=true}} @opts={{hash removeMissing=true}}
@onOffsetHeightCalculated={{this.setPostBodyHeight}}
/> />
{{#if this.isLongPost}}
<DButton
@class="btn-default btn-icon post-body__toggle-btn"
@action={{this.toggleContent}}
@label={{this.collapseButtonProps.label}}
@icon={{this.collapseButtonProps.icon}}
/>
{{/if}}
{{yield}} {{yield}}
</div> </div>
</div> </div>

View File

@ -1,11 +1,51 @@
import Component from "@ember/component"; import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { action } from "@ember/object"; import { action } from "@ember/object";
import showModal from "discourse/lib/show-modal"; import showModal from "discourse/lib/show-modal";
export default Component.extend({ export default class ReviewableQueuedPost extends Component {
@tracked isCollapsed = false;
@tracked isLongPost = false;
@tracked postBodyHeight = 0;
maxPostHeight = 300;
@action @action
showRawEmail(event) { showRawEmail(event) {
event?.preventDefault(); event?.preventDefault();
showModal("raw-email").set("rawEmail", this.reviewable.payload.raw_email); showModal("raw-email").set(
}, "rawEmail",
}); this.args.reviewable.payload.raw_email
);
}
@action
toggleContent() {
this.isCollapsed = !this.isCollapsed;
}
get collapseButtonProps() {
if (this.isCollapsed) {
return {
label: "review.show_more",
icon: "chevron-down",
};
}
return {
label: "review.show_less",
icon: "chevron-up",
};
}
@action
setPostBodyHeight(offsetHeight) {
this.postBodyHeight = offsetHeight;
if (this.postBodyHeight > this.maxPostHeight) {
this.isCollapsed = true;
this.isLongPost = true;
} else {
this.isCollapsed = false;
this.isLongPost = false;
}
}
}

View File

@ -14,7 +14,7 @@ module("Integration | Component | cook-text", function (hooks) {
}); });
test("renders markdown", async function (assert) { test("renders markdown", async function (assert) {
await render(hbs`<CookText @rawText="_foo_" @class="post-body" />`); await render(hbs`<CookText @rawText="_foo_" class="post-body" />`);
const html = query(".post-body").innerHTML.trim(); const html = query(".post-body").innerHTML.trim();
assert.strictEqual(html, "<p><em>foo</em></p>"); assert.strictEqual(html, "<p><em>foo</em></p>");
@ -32,7 +32,7 @@ module("Integration | Component | cook-text", function (hooks) {
); );
await render( await render(
hbs`<CookText @rawText="![an image](upload://a.png)" @class="post-body" />` hbs`<CookText @rawText="![an image](upload://a.png)" class="post-body" />`
); );
const html = query(".post-body").innerHTML.trim(); const html = query(".post-body").innerHTML.trim();

View File

@ -52,7 +52,7 @@ module I18n
# load it # load it
I18n.backend.load_translations(I18n.load_path.grep(/\.#{Regexp.escape locale}\.yml\z/)) I18n.backend.load_translations(I18n.load_path.grep(/\.#{Regexp.escape locale}\.yml\z/))
if Discourse.allow_dev_populate? if Discourse.allow_dev_populate? || Rails.env.test? || Rails.env.development?
I18n.backend.load_translations( I18n.backend.load_translations(
I18n.load_path.grep(%r{.*faker.*/#{Regexp.escape locale}\.yml\z}), I18n.load_path.grep(%r{.*faker.*/#{Regexp.escape locale}\.yml\z}),
) )

View File

@ -1,5 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
require "faker"
Fabricator(:reviewable) do Fabricator(:reviewable) do
reviewable_by_moderator true reviewable_by_moderator true
type "ReviewableUser" type "ReviewableUser"
@ -59,6 +61,36 @@ Fabricator(:reviewable_queued_post) do
end end
end end
Fabricator(:reviewable_queued_long_post, from: :reviewable_queued_post) do
reviewable_by_moderator true
type "ReviewableQueuedPost"
created_by { Fabricate(:user) }
target_created_by { Fabricate(:user) }
topic
payload do
{
raw: Faker::DiscourseMarkdown.sandwich(sentences: 6, repeat: 3),
reply_to_post_number: 1,
via_email: true,
raw_email: "store_me",
auto_track: true,
custom_fields: {
hello: "world",
},
cooking_options: {
cat: "hat",
},
cook_method: Post.cook_methods[:raw_html],
image_sizes: {
"http://foo.bar/image.png" => {
"width" => 0,
"height" => 222,
},
},
}
end
end
Fabricator(:reviewable_flagged_post) do Fabricator(:reviewable_flagged_post) do
reviewable_by_moderator true reviewable_by_moderator true
type "ReviewableFlaggedPost" type "ReviewableFlaggedPost"

View File

@ -8,7 +8,7 @@ describe "Reviewables", type: :system do
before { sign_in(admin) } before { sign_in(admin) }
describe "when there is a reviewable with a long post" do describe "when there is a flagged post reviewable with a long post" do
fab!(:long_reviewable) { Fabricate(:reviewable_flagged_post, target: long_post) } fab!(:long_reviewable) { Fabricate(:reviewable_flagged_post, target: long_post) }
it "should show a button to expand/collapse the post content" do it "should show a button to expand/collapse the post content" do
@ -22,7 +22,7 @@ describe "Reviewables", type: :system do
end end
end end
describe "when there is a reviewable with a short post" do describe "when there is a flagged post reviewable with a short post" do
fab!(:short_reviewable) { Fabricate(:reviewable_flagged_post) } fab!(:short_reviewable) { Fabricate(:reviewable_flagged_post) }
it "should not show a button to expand/collapse the post content" do it "should not show a button to expand/collapse the post content" do
@ -32,6 +32,30 @@ describe "Reviewables", type: :system do
end end
end end
describe "when there is a queued post reviewable with a short post" do
fab!(:short_queued_reviewable) { Fabricate(:reviewable_queued_post) }
it "should not show a button to expand/collapse the post content" do
visit("/review")
expect(review_page).to have_no_post_body_collapsed
expect(review_page).to have_no_post_body_toggle
end
end
describe "when there is a queued post reviewable with a long post" do
fab!(:long_queued_reviewable) { Fabricate(:reviewable_queued_long_post) }
it "should show a button to expand/collapse the post content" do
visit("/review")
expect(review_page).to have_post_body_collapsed
expect(review_page).to have_post_body_toggle
review_page.click_post_body_toggle
expect(review_page).to have_no_post_body_collapsed
review_page.click_post_body_toggle
expect(review_page).to have_post_body_collapsed
end
end
context "when performing a review action from the show route" do context "when performing a review action from the show route" do
context "with a ReviewableQueuedPost" do context "with a ReviewableQueuedPost" do
fab!(:queued_post_reviewable) { Fabricate(:reviewable_queued_post) } fab!(:queued_post_reviewable) { Fabricate(:reviewable_queued_post) }