From 1b57e568ffaaa7ed5a7d0b175547c95332319c3e Mon Sep 17 00:00:00 2001 From: Steve Howell Date: Fri, 23 Feb 2018 09:37:19 -0500 Subject: [PATCH] Add "zform" functionality to the web client. A "zform" knows how to render data that follows our schema for widget messages with form elements like buttons and choices. This code won't be triggered until a subsequent server-side commit takes widget_content from API callers such as the trivial chat bot and creates submessages for us. --- .eslintrc.json | 1 + static/js/widgetize.js | 1 + static/js/zform.js | 115 ++++++++++++++++++ static/styles/widgets.scss | 17 +++ .../widgets/zform-choices.handlebars | 12 ++ tools/webpack.assets.json | 3 +- zproject/settings.py | 1 + 7 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 static/js/zform.js create mode 100644 static/styles/widgets.scss create mode 100644 static/templates/widgets/zform-choices.handlebars diff --git a/.eslintrc.json b/.eslintrc.json index 7c41b81390..008e68b3a5 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -175,6 +175,7 @@ "user_events": false, "voting_widget": false, "tictactoe_widget": false, + "zform": false, "widgetize": false, "submessage": false, "Plotly": false, diff --git a/static/js/widgetize.js b/static/js/widgetize.js index eae2f497c2..e9e2a4a5cf 100644 --- a/static/js/widgetize.js +++ b/static/js/widgetize.js @@ -6,6 +6,7 @@ var widgets = {}; widgets.poll = voting_widget; widgets.tictactoe = tictactoe_widget; +widgets.zform = zform; exports.activate = function (in_opts) { var widget_type = in_opts.widget_type; diff --git a/static/js/zform.js b/static/js/zform.js new file mode 100644 index 0000000000..3fe04b1d44 --- /dev/null +++ b/static/js/zform.js @@ -0,0 +1,115 @@ +var zform = (function () { + +var exports = {}; + +exports.validate_extra_data = function (data) { + function check(data) { + function check_choice_data(data) { + function check_choice_item(field_name, val) { + return schema.check_record(field_name, val, { + short_name: schema.check_string, + long_name: schema.check_string, + reply: schema.check_string, + }); + } + + function check_choices(field_name, val) { + return schema.check_array( + field_name, + val, + check_choice_item + ); + } + + return schema.check_record('zform data', data, { + heading: schema.check_string, + choices: check_choices, + }); + } + + if (data.type === 'choices') { + return check_choice_data(data); + } + + return 'unknown zform type: ' + data.type; + } + + + var msg = check(data); + + if (msg) { + blueslip.warn(msg); + return false; + } + + return true; +}; + +exports.activate = function (opts) { + var self = {}; + + var outer_elem = opts.elem; + var data = opts.extra_data; + + if (!exports.validate_extra_data(data)) { + // callee will log reason we fail + return; + } + + function make_choices(data) { + // Assign idx values to each of our choices so that + // our template can create data-idx values for our + // JS code to use later. + _.each(data.choices, function (choice, idx) { + choice.idx = idx; + }); + + var html = templates.render('zform-choices', data); + var elem = $(html); + + elem.find('button').on('click', function (e) { + e.stopPropagation(); + + // Grab our index from the markup. + var idx = $(e.target).attr('data-idx'); + + // Use the index from the markup to dereference our + // data structure. + var reply_content = data.choices[idx].reply; + + transmit.reply_message({ + message: opts.message, + content: reply_content, + }); + }); + + return elem; + } + + function render() { + var rendered_widget; + + if (data.type === 'choices') { + rendered_widget = make_choices(data); + outer_elem.html(rendered_widget); + } + } + + self.handle_events = function (events) { + if (events) { + blueslip.info('unexpected'); + } + render(); + }; + + render(); + + return self; +}; + +return exports; + +}()); +if (typeof module !== 'undefined') { + module.exports = zform; +} diff --git a/static/styles/widgets.scss b/static/styles/widgets.scss new file mode 100644 index 0000000000..7ecdf3bc1f --- /dev/null +++ b/static/styles/widgets.scss @@ -0,0 +1,17 @@ +.widget-choices ul { + padding: 3px; +} + +.widget-choices li { + padding: 2px; + list-style: none; +} + +.widget-choices button { + font-weight: 700; + color: blue; +} + +.widget-choices-heading { + font-weight: 600; +} diff --git a/static/templates/widgets/zform-choices.handlebars b/static/templates/widgets/zform-choices.handlebars new file mode 100644 index 0000000000..8a892501f5 --- /dev/null +++ b/static/templates/widgets/zform-choices.handlebars @@ -0,0 +1,12 @@ +
+
{{ heading }}
+ +
diff --git a/tools/webpack.assets.json b/tools/webpack.assets.json index 5d7533975e..82ab9ce2f4 100644 --- a/tools/webpack.assets.json +++ b/tools/webpack.assets.json @@ -79,7 +79,8 @@ "./static/styles/media.scss", "./static/styles/typing_notifications.scss", "./static/styles/hotspots.scss", - "./static/styles/night_mode.scss" + "./static/styles/night_mode.scss", + "./static/styles/widgets.scss" ], "archive-styles": [ "./node_modules/katex/dist/katex.css", diff --git a/zproject/settings.py b/zproject/settings.py index fd13dbbb00..b91e8b9be8 100644 --- a/zproject/settings.py +++ b/zproject/settings.py @@ -964,6 +964,7 @@ JS_SPECS = { 'js/filter.js', 'js/voting_widget.js', 'js/tictactoe_widget.js', + 'js/zform.js', 'js/widgetize.js', 'js/submessage.js', 'js/fetch_status.js',