Skip to content

Commit 5d20f81

Browse files
authored
[BB-10108] Refactor Branching XBlock to include Ren'Py like features (#10)
- Add overlay option for image node - Change choices to radio single choice - Updated studio editor - Composite images + styling - Add scores to each choice - Hide choices only if no branches flag is selected - Add soft delete for nodes - Display unlinked nodes - Prevent cyclic nodes - Compute maximum attainable score in scenario using topo+dp to find max path sum - Add grade-range slider - Final grade report
1 parent 067ffe4 commit 5d20f81

14 files changed

+3809
-525
lines changed

branching_xblock/branching_xblock.py

Lines changed: 819 additions & 99 deletions
Large diffs are not rendered by default.

branching_xblock/static/css/branching_xblock.css

Lines changed: 559 additions & 42 deletions
Large diffs are not rendered by default.

branching_xblock/static/css/studio_editor.css

Lines changed: 557 additions & 149 deletions
Large diffs are not rendered by default.
Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,35 @@
11
<div class="choice-row" data-choice-idx="{{i}}">
2-
<input class="choice-text" value="{{choice.text}}" placeholder="Choice text"/>
3-
<select class="choice-target">
4-
{{#each options}}
5-
<option value="{{this.id}}" {{#if (eq this.id ../choice.target_node_id)}}selected{{/if}}>{{this.label}}</option>
6-
{{/each}}
7-
</select>
8-
<button type="button" class="btn-delete-choice">x</button>
2+
<div class="choice-col choice-col--text">
3+
<label class="choice-col__label">Choice text</label>
4+
<input class="choice-text" value="{{choice.text}}" placeholder="Choice text"/>
5+
</div>
6+
<div class="choice-col choice-col--score">
7+
<label class="choice-col__label">Score</label>
8+
<input
9+
class="choice-score {{#if score_error}}is-error{{/if}}"
10+
type="number"
11+
min="0"
12+
max="100"
13+
step="1"
14+
value="{{choice.score}}"
15+
placeholder="0"
16+
aria-label="Choice score"
17+
/>
18+
{{#if score_error}}
19+
<div class="choice-field-error">{{score_error}}</div>
20+
{{/if}}
21+
</div>
22+
<div class="choice-col choice-col--target">
23+
<label class="choice-col__label">Destination*</label>
24+
<select class="choice-target {{#if destination_error}}is-error{{/if}}">
25+
<option value="" {{#unless choice.target_node_id}}selected{{/unless}}>Select node</option>
26+
{{#each options}}
27+
<option value="{{this.id}}" {{#if (eq this.id ../choice.target_node_id)}}selected{{/if}}>{{this.label}}</option>
28+
{{/each}}
29+
</select>
30+
{{#if destination_error}}
31+
<div class="choice-field-error">{{destination_error}}</div>
32+
{{/if}}
33+
</div>
34+
<button type="button" class="btn-delete-choice" aria-label="Delete choice">×</button>
935
</div>

branching_xblock/static/handlebars/node-block.handlebars

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@
2222
</label>
2323
</div>
2424
</label>
25+
<div class="overlay-text-control {{#unless (eq node.media.type 'image')}}is-hidden{{/unless}}">
26+
<label class="overlay-text-toggle">
27+
<input type="checkbox" class="overlay-text-checkbox" {{#if node.overlay_text}}checked{{/if}}>
28+
Overlay text on image
29+
</label>
30+
<p class="overlay-text-help">If left unchecked, text will appear outside the image.</p>
31+
</div>
2532
<label>Hint:<br/>
2633
<textarea class="node-hint">{{node.hint}}</textarea>
2734
</label>
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<div class="bx-node-editor-inner" data-node-id="{{id}}">
2+
{{#if node_error_detail}}
3+
<div class="bx-node-error-banner" role="alert">
4+
<span class="fa fa-exclamation-circle bx-node-error-banner__icon" aria-hidden="true"></span>
5+
<div class="bx-node-error-banner__content">
6+
<div class="bx-node-error-banner__title">
7+
{{#if node_error_title}}{{node_error_title}}{{else}}You can't delete this node{{/if}}
8+
</div>
9+
<div class="bx-node-error-banner__detail">{{node_error_detail}}</div>
10+
</div>
11+
</div>
12+
{{/if}}
13+
14+
<div class="bx-node-editor-header">
15+
<h2 class="bx-step-title">Node {{number}}</h2>
16+
{{#if is_pending_delete}}
17+
<span class="bx-pending-delete-badge">Pending deletion</span>
18+
{{/if}}
19+
</div>
20+
21+
<label class="bx-field">
22+
<div class="bx-field__label">Content</div>
23+
<textarea class="bx-textarea" data-role="node-content">{{content}}</textarea>
24+
</label>
25+
26+
<label class="bx-field">
27+
<div class="bx-field__label">Media</div>
28+
<select class="bx-select" data-role="media-type">
29+
<option value="" {{#unless media.type}}selected{{/unless}}>None</option>
30+
<option value="image" {{#if (eq media.type 'image')}}selected{{/if}}>Image</option>
31+
<option value="video" {{#if (eq media.type 'video')}}selected{{/if}}>Video</option>
32+
<option value="audio" {{#if (eq media.type 'audio')}}selected{{/if}}>Audio</option>
33+
</select>
34+
</label>
35+
36+
<div class="{{#unless is_image}}is-hidden{{/unless}}" data-role="image-url-fields">
37+
<label class="bx-field">
38+
<div class="bx-field__label">Left image URL</div>
39+
<input type="text" class="bx-input {{#if left_image_url_error}}is-error{{/if}}" data-role="left-image-url" placeholder="URL" value="{{left_image_url}}" />
40+
{{#if left_image_url_error}}
41+
<div class="bx-field-error">{{left_image_url_error}}</div>
42+
{{/if}}
43+
</label>
44+
<label class="bx-field">
45+
<div class="bx-field__label">Right image URL</div>
46+
<input type="text" class="bx-input" data-role="right-image-url" placeholder="URL" value="{{right_image_url}}" />
47+
</label>
48+
</div>
49+
50+
<label class="bx-field {{#unless show_media_url}}is-hidden{{/unless}}" data-role="media-url-field">
51+
<div class="bx-field__label">URL</div>
52+
<input type="text" class="bx-input" data-role="media-url" placeholder="URL" value="{{media.url}}" />
53+
<div class="bx-help">Supports direct media files (.mp4/.webm/.mp3) or links from YouTube, Vimeo, Panopto.</div>
54+
</label>
55+
56+
<label class="bx-field {{#unless show_transcript}}is-hidden{{/unless}}" data-role="transcript-url-field">
57+
<div class="bx-field__label">Transcript URL</div>
58+
<input type="text" class="bx-input" data-role="transcript-url" placeholder="https://..." value="{{transcript_url}}" />
59+
</label>
60+
61+
<div class="overlay-text-control {{#unless show_overlay}}is-hidden{{/unless}}" data-role="overlay-text-control">
62+
<label class="overlay-text-toggle">
63+
<input type="checkbox" class="overlay-text-checkbox" data-role="overlay-text" {{#if overlay_text}}checked{{/if}}>
64+
Overlay text on image
65+
</label>
66+
<p class="overlay-text-help">If left unchecked, text will appear outside the image.</p>
67+
</div>
68+
69+
<label class="bx-checkbox">
70+
<input type="checkbox" data-role="no-branches" {{#if no_branches}}checked{{/if}} {{#if has_choices}}disabled{{/if}} />
71+
<span>This node has no branches</span>
72+
</label>
73+
74+
<label class="bx-field">
75+
<div class="bx-field__label">Hint</div>
76+
<textarea class="bx-textarea" data-role="node-hint">{{hint}}</textarea>
77+
</label>
78+
79+
<div class="bx-choices {{#if no_branches}}is-hidden{{/if}}" data-role="choices-section">
80+
<h3 class="bx-section-title">Choices</h3>
81+
<div class="bx-help bx-choices-help">When a learner selects a choice, the score assigned to that choice is added to the total score.</div>
82+
<div class="choices-container" data-role="choices-container"></div>
83+
<button type="button" class="bx-btn bx-btn--secondary" data-role="add-choice">Add Choice</button>
84+
</div>
85+
</div>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<div class="bx-node-list-item {{#if is_selected}}is-selected{{/if}} {{#if is_pending_delete}}is-pending-delete{{/if}} {{#if has_errors}}has-errors{{/if}}" data-node-id="{{id}}">
2+
<button type="button" class="bx-node-list-item__select" data-role="select-node" data-node-id="{{id}}">
3+
Node {{number}}{{#if is_unlinked}} <span class="bx-node-list-item__meta">(unlinked node)</span>{{/if}}
4+
</button>
5+
{{#if has_errors}}
6+
<span class="bx-node-list-item__error" aria-label="Node has errors">!</span>
7+
{{/if}}
8+
<button
9+
type="button"
10+
class="bx-node-list-item__delete {{#if is_pending_delete}}is-restore{{/if}}"
11+
data-role="toggle-delete-node"
12+
data-node-id="{{id}}"
13+
aria-label="{{#if is_pending_delete}}Restore node{{else}}Delete node{{/if}}"
14+
>
15+
{{#if is_pending_delete}}
16+
<span class="fa fa-undo" aria-hidden="true"></span>
17+
{{else}}
18+
<span class="fa fa-trash-o" aria-hidden="true"></span>
19+
{{/if}}
20+
</button>
21+
</div>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<div class="bx-wizard bx-nodes-step">
2+
<div class="bx-nodes-sidebar">
3+
<button type="button" class="bx-btn bx-btn--primary" data-role="add-node">+ Add node</button>
4+
<div class="bx-node-limit">Max 30 nodes</div>
5+
<div class="bx-node-list" data-role="node-list"></div>
6+
</div>
7+
8+
<div class="bx-nodes-main">
9+
<div class="bx-node-editor" data-role="node-editor"></div>
10+
</div>
11+
</div>
12+
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<div class="bx-wizard bx-settings-step">
2+
<h2 class="bx-step-title">Settings</h2>
3+
4+
<label class="bx-field">
5+
<div class="bx-field__label">Display Name</div>
6+
<input type="text" name="display_name" value="{{display_name}}" class="bx-input" />
7+
</label>
8+
9+
<label class="bx-checkbox">
10+
<input type="checkbox" name="enable_undo" {{#if enable_undo}}checked{{/if}} />
11+
<span>Let learners try the previous node again</span>
12+
</label>
13+
14+
<label class="bx-checkbox">
15+
<input type="checkbox" name="enable_reset_activity" {{#if enable_reset_activity}}checked{{/if}} />
16+
<span>Let learners reset the activity</span>
17+
</label>
18+
19+
<label class="bx-checkbox">
20+
<input type="checkbox" name="enable_scoring" {{#if enable_scoring}}checked{{/if}} />
21+
<span>Include a grade report at the end of the activity</span>
22+
</label>
23+
24+
<div class="bx-grade-range {{#unless enable_scoring}}is-hidden{{/unless}}" data-role="grade-range-section">
25+
<h3 class="bx-section-title bx-grade-range__title">Specify Grade Range</h3>
26+
<p class="bx-help bx-grade-range__help">Adjust the scale to set percentage ranges for each grade.</p>
27+
<div class="bx-grade-range__slider" data-role="grade-range-slider"></div>
28+
{{#if grade_ranges_error}}
29+
<div class="bx-field-error">{{grade_ranges_error}}</div>
30+
{{/if}}
31+
</div>
32+
33+
<h3 class="bx-section-title">Background image</h3>
34+
<p class="bx-help">
35+
This image will appear in the background of any nodes with “Image” as the media type.
36+
</p>
37+
38+
<label class="bx-field">
39+
<div class="bx-field__label">Image URL</div>
40+
<input
41+
type="text"
42+
name="background_image_url"
43+
class="bx-input {{#if background_image_url_error}}is-error{{/if}}"
44+
placeholder="URL"
45+
value="{{background_image_url}}"
46+
/>
47+
{{#if background_image_url_error}}
48+
<div class="bx-field-error">{{background_image_url_error}}</div>
49+
{{/if}}
50+
</label>
51+
52+
<div class="bx-field">
53+
<div class="bx-field__label">Alt text</div>
54+
<label class="bx-checkbox bx-checkbox--nested">
55+
<input type="checkbox" name="background_image_is_decorative" {{#if background_image_is_decorative}}checked{{/if}} />
56+
<span>Decorative image</span>
57+
</label>
58+
<input
59+
type="text"
60+
name="background_image_alt_text"
61+
class="bx-input {{#if background_image_alt_text_error}}is-error{{/if}}"
62+
placeholder="Alt text"
63+
value="{{background_image_alt_text}}"
64+
{{#if background_image_is_decorative}}disabled{{/if}}
65+
/>
66+
{{#if background_image_alt_text_error}}
67+
<div class="bx-field-error">{{background_image_alt_text_error}}</div>
68+
{{/if}}
69+
</div>
70+
</div>

branching_xblock/static/html/branching_xblock.html

Lines changed: 79 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,87 @@
1111
<div data-role="hint" class="node-hint px-2 py-3"></div>
1212
</details>
1313
</div>
14-
<div class="choices" data-role="choices"></div>
15-
16-
<div class="action-row">
17-
<button class="undo-button action-secondary btn-secondary" data-role="undo" style="display: none;">
18-
Go Back
19-
</button>
20-
<button type="button" class="reset-button action-secondary btn-secondary" data-role="reset-activity" style="display: none;">
21-
Reset
22-
</button>
14+
<div class="choices" data-role="choices">
15+
<h3 class="choices-heading" data-role="choice-heading">Choose Next Step:</h3>
16+
<form class="choices-form" data-role="choice-form">
17+
<div class="choices-list" data-role="choice-list"></div>
18+
<div class="choice-actions">
19+
<div class="choice-actions__secondary">
20+
<button type="button" class="undo-button action-secondary btn-secondary" data-role="undo">
21+
Go Back
22+
</button>
23+
<button type="button" class="reset-button action-secondary btn-secondary" data-role="reset-activity" style="display: none;">
24+
Reset
25+
</button>
26+
</div>
27+
<div class="choice-actions__primary">
28+
<button type="button" class="show-report-button action-primary btn-primary" data-role="show-report" style="display: none;">
29+
Show Report
30+
</button>
31+
<button type="submit" class="choice-submit-button action-primary btn-primary" data-role="submit-choice" disabled>
32+
Submit
33+
</button>
34+
</div>
35+
</div>
36+
</form>
2337
</div>
2438

2539
<div class="score-display" data-role="score" style="display: none;"></div>
40+
<div class="grade-report" data-role="grade-report" hidden>
41+
<h3 class="grade-report__title">Activity Complete!</h3>
42+
<p class="grade-report__subtitle">Let's take a look at this report below to see how you performed.</p>
43+
44+
<div class="grade-report__summary-card">
45+
<h4 class="grade-report__section-title">
46+
<span class="grade-report__section-icon" aria-hidden="true">
47+
<span class="fa fa-graduation-cap" aria-hidden="true"></span>
48+
</span>
49+
Your Grade
50+
</h4>
51+
<div class="grade-report__summary">
52+
<div class="grade-report__metric grade-report__metric--score">
53+
<div class="grade-report__metric-label">
54+
<span class="grade-report__metric-label-icon" aria-hidden="true">
55+
<span class="fa fa-trophy" data-role="report-score-icon" aria-hidden="true"></span>
56+
</span>
57+
Your Score
58+
</div>
59+
<div class="grade-report__metric-value" data-role="report-score">0</div>
60+
</div>
61+
<div class="grade-report__metric grade-report__metric--max">
62+
<div class="grade-report__metric-label">Highest Possible Score</div>
63+
<div class="grade-report__metric-value" data-role="report-max-score">0</div>
64+
</div>
65+
<div class="grade-report__percent" data-role="report-percent-pill">
66+
<svg class="grade-report__percent-ring" viewBox="0 0 120 120" aria-hidden="true" focusable="false">
67+
<circle class="grade-report__percent-track" cx="60" cy="60" r="48"></circle>
68+
<circle class="grade-report__percent-progress" data-role="report-percent-circle" cx="60" cy="60" r="48"></circle>
69+
</svg>
70+
<div class="grade-report__percent-content">
71+
<div class="grade-report__percent-value" data-role="report-percent">0%</div>
72+
<div class="grade-report__percent-label" data-role="report-grade-label"></div>
73+
</div>
74+
</div>
75+
</div>
76+
</div>
77+
78+
<div class="grade-report__details">
79+
<h4 class="grade-report__details-title">
80+
<span class="grade-report__section-icon" aria-hidden="true">
81+
<span class="fa fa-line-chart" aria-hidden="true"></span>
82+
</span>
83+
Detailed Score
84+
</h4>
85+
<div class="grade-report__details-header">
86+
<span>Your Selections</span>
87+
<span>Score</span>
88+
</div>
89+
<div class="grade-report__details-rows" data-role="report-details"></div>
90+
</div>
91+
92+
<button type="button" class="action-primary btn-primary" data-role="reset-activity-report" style="display: none;">
93+
Reset Activity
94+
</button>
95+
</div>
2696
</div>
2797
</div>

0 commit comments

Comments
 (0)