Skip to content

Commit

Permalink
widgets: Support markdown in widgets.
Browse files Browse the repository at this point in the history
Previously, markdown was not supported in widgets like
polls and todo. This is fixed by adding an inline only
backend processor ZulipInlineMarkdown, and using
it to render the poll options or tasks and descriptions
of todo before adding them to the database.

Fixes zulip#21917
  • Loading branch information
roanster007 committed Feb 11, 2024
1 parent d3b5a76 commit cf1080f
Show file tree
Hide file tree
Showing 14 changed files with 563 additions and 30 deletions.
24 changes: 22 additions & 2 deletions web/src/poll_widget.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import {isValid, parseISO} from "date-fns";
import $ from "jquery";

import {PollData} from "../shared/src/poll_data";
Expand All @@ -7,6 +8,7 @@ import type {
QuestionOutboundData,
VoteOutboundData,
} from "../shared/src/poll_data";
import render_markdown_timestamp from "../templates/markdown_timestamp.hbs";
import render_widgets_poll_widget from "../templates/widgets/poll_widget.hbs";
import render_widgets_poll_widget_results from "../templates/widgets/poll_widget_results.hbs";

Expand All @@ -15,6 +17,7 @@ import {$t} from "./i18n";
import * as keydown_util from "./keydown_util";
import type {Message} from "./message_store";
import * as people from "./people";
import * as timerender from "./timerender";

type Event = {sender_id: number; data: InboundData};

Expand Down Expand Up @@ -225,10 +228,9 @@ export function activate({

function render_results(): void {
const widget_data = poll_data.get_widget_data();

const html = render_widgets_poll_widget_results(widget_data);
$elem.find("ul.poll-widget").html(html);

render_poll_timestamp($elem);
$elem
.find("button.poll-vote")
.off("click")
Expand All @@ -239,6 +241,24 @@ export function activate({
});
}

function render_poll_timestamp($elem: JQuery): void {
$elem.find("ul.poll-widget time").each(function () {
const time_str = $(this).attr("datetime");
if (time_str === undefined) {
return;
}
const timestamp = parseISO(time_str);
if (isValid(timestamp)) {
const rendered_timestamp = render_markdown_timestamp({
text: timerender.format_markdown_time(timestamp),
});
$(this).html(rendered_timestamp);
} else {
blueslip.error("Could not parse datetime supplied by backend", {time_str});
}
});
}

$elem.handle_events = function (events: Event[]) {
for (const event of events) {
poll_data.handle_event(event.sender_id, event.data);
Expand Down
23 changes: 22 additions & 1 deletion web/src/todo_widget.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import {isValid, parseISO} from "date-fns";
import $ from "jquery";

import render_markdown_timestamp from "../templates/markdown_timestamp.hbs";
import render_widgets_todo_widget from "../templates/widgets/todo_widget.hbs";
import render_widgets_todo_widget_tasks from "../templates/widgets/todo_widget_tasks.hbs";

import * as blueslip from "./blueslip";
import {$t} from "./i18n";
import {page_params} from "./page_params";
import * as people from "./people";

import * as timerender from "./timerender";
// Any single user should send add a finite number of tasks
// to a todo list. We arbitrarily pick this value.
const MAX_IDX = 1000;
Expand Down Expand Up @@ -183,6 +185,7 @@ export function activate(opts) {
const widget_data = task_data.get_widget_data();
const html = render_widgets_todo_widget_tasks(widget_data);
$elem.find("ul.todo-widget").html(html);
render_todo_timestamp($elem);
$elem.find(".widget-error").text("");

$elem.find("input.task").on("click", (e) => {
Expand All @@ -205,6 +208,24 @@ export function activate(opts) {
});
}

function render_todo_timestamp($elem) {
$elem.find("ul.todo-widget time").each(function () {
const time_str = $(this).attr("datetime");
if (time_str === undefined) {
return;
}
const timestamp = parseISO(time_str);
if (isValid(timestamp)) {
const rendered_timestamp = render_markdown_timestamp({
text: timerender.format_markdown_time(timestamp),
});
$(this).html(rendered_timestamp);
} else {
blueslip.error("Could not parse datetime supplied by backend", {time_str});
}
});
}

$elem.handle_events = function (events) {
for (const event of events) {
task_data.handle_event(event.sender_id, event.data);
Expand Down
24 changes: 22 additions & 2 deletions web/styles/widgets.css
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,25 @@
position: relative;
vertical-align: top;

.task,
.task_desc,
.description {
display: inline-block;
}

.task_complete,
.task_desc_complete,
.description_complete {
display: inline-block;
text-decoration: line-through;
}

& input[type="checkbox"] {
~ span {
height: 12px;
width: 12px;
border: 2px solid hsl(156deg 28% 70%);
top: 2px;

border-radius: 4px;
filter: brightness(1);
Expand Down Expand Up @@ -122,6 +136,8 @@
.poll-option {
font-weight: 400;
font-size: 14px;
padding-top: 3px;
display: inline;
}

/* For the box-shadow to be visible on the left */
Expand All @@ -131,7 +147,6 @@

& span.poll-option {
font-weight: 600;
padding-top: 28px;
}

.poll-vote {
Expand Down Expand Up @@ -166,8 +181,13 @@
.poll-names {
color: hsl(0deg 0% 45%);
padding-left: 4px;
padding-top: 28px;
padding-top: 3px;
font-size: 14px;
display: inline;
}

.poll-option-content {
display: inline;
}
}

Expand Down
10 changes: 6 additions & 4 deletions web/templates/widgets/poll_widget_results.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
<button class="poll-vote {{#if current_user_vote}}current-user-vote{{/if}}" data-key="{{ key }}">
{{ count }}
</button>
<span class="poll-option">{{ option }}</span>
{{#if names}}
<span class="poll-names">({{ names }})</span>
{{/if}}
<span class="poll-option">
{{{ option }}}
{{#if names}}
<span class="poll-names">({{ names }})</span>
{{/if}}
</span>
</li>
{{/each}}
4 changes: 2 additions & 2 deletions web/templates/widgets/todo_widget_tasks.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
</div>
<div>
{{#if completed}}
<strike><strong>{{ task }}</strong>{{#if desc }}: {{ desc }}{{/if}}</strike>
<strike><strong class="task_complete">{{{ task }}}</strong>{{#if desc }}<span class="task_desc_complete">: </span><span class="description_complete">{{{ desc }}}</span>{{/if}}</strike>
{{else}}
<strong>{{ task }}</strong>{{#if desc }}: {{ desc }}{{/if}}
<strong class="task">{{{ task }}}</strong>{{#if desc }}<span class="task_desc">: </span><span class="description">{{{ desc }}}</span>{{/if}}
{{/if}}
</div>
</label>
Expand Down
2 changes: 1 addition & 1 deletion web/tests/markdown.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ test("marked_shared", () => {
for (const test of tests) {
// Ignore tests if specified
/* istanbul ignore if */
if (test.ignore === true) {
if (test.ignore === true || test.markdown === "Inline") {
continue;
}

Expand Down
4 changes: 4 additions & 0 deletions web/tests/poll_widget.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,8 @@ run_test("activate another person poll", ({mock_template}) => {
const $poll_question_header = set_widget_find_result(".poll-question-header");
const $poll_question_container = set_widget_find_result(".poll-question-bar");
const $poll_option_container = set_widget_find_result(".poll-option-bar");
set_widget_find_result("ul.poll-widget time");
$("ul.poll-widget time").each = function () {};

const $poll_vote_button = set_widget_find_result("button.poll-vote");
const $poll_please_wait = set_widget_find_result(".poll-please-wait");
Expand Down Expand Up @@ -350,6 +352,8 @@ run_test("activate own poll", ({mock_template}) => {
set_widget_find_result("button.poll-option");
const $poll_option_input = set_widget_find_result("input.poll-option");
const $widget_option_container = set_widget_find_result("ul.poll-widget");
set_widget_find_result("ul.poll-widget time");
$("ul.poll-widget time").each = function () {};

const $poll_question_submit = set_widget_find_result("button.poll-question-check");
const $poll_edit_question = set_widget_find_result(".poll-edit-question");
Expand Down
Loading

0 comments on commit cf1080f

Please sign in to comment.