Decaffeinate ui/features/grade_summary

Refs FOO-3200
flag=none

Remove all CoffeeScript from this feature bundle.

Test plan:
* all tests pass

Change-Id: I2d0e495699931a9963c49816d2adcffec5e92142
Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/304696
Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com>
Reviewed-by: Cameron Ray <cameron.ray@instructure.com>
QA-Review: Cameron Ray <cameron.ray@instructure.com>
Product-Review: Charley Kline <ckline@instructure.com>
This commit is contained in:
Charley Kline 2022-11-03 16:23:40 -05:00
parent 03ffd9d120
commit c08fe4afdc
30 changed files with 919 additions and 796 deletions

View File

@ -18,7 +18,7 @@
import Backbone from '@canvas/backbone'
import Outcome from '@canvas/grade-summary/backbone/models/Outcome.coffee'
import OutcomeResultCollection from 'ui/features/grade_summary/backbone/collections/OutcomeResultCollection.coffee'
import OutcomeResultCollection from 'ui/features/grade_summary/backbone/collections/OutcomeResultCollection'
import fakeENV from 'helpers/fakeENV'
import tz from '@canvas/timezone'
@ -96,6 +96,7 @@ QUnit.module('OutcomeResultCollectionSpec', {
})
test('default params reflect aligned outcome', function () {
// eslint-disable-next-line new-cap
const collectionModel = new this.outcomeResultCollection.model()
deepEqual(collectionModel.get('mastery_points'), 8)
deepEqual(collectionModel.get('points_possible'), 10)

View File

@ -18,9 +18,9 @@
import Backbone from '@canvas/backbone'
import CollectionView from '@canvas/backbone-collection-view'
import OutcomeResultCollection from 'ui/features/grade_summary/backbone/collections/OutcomeResultCollection.coffee'
import OutcomeResultCollection from 'ui/features/grade_summary/backbone/collections/OutcomeResultCollection'
import Outcome from '@canvas/grade-summary/backbone/models/Outcome.coffee'
import Group from 'ui/features/grade_summary/backbone/models/Group.coffee'
import Group from 'ui/features/grade_summary/backbone/models/Group'
import OutcomeDetailView from 'ui/features/grade_summary/backbone/views/OutcomeDetailView'
import fakeENV from 'helpers/fakeENV'

View File

@ -16,10 +16,11 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import $ from 'jquery'
import _ from 'underscore'
import Outcome from '@canvas/grade-summary/backbone/models/Outcome.coffee'
import OutcomeDialogView from 'ui/features/grade_summary/backbone/views/OutcomeDialogView.coffee'
import OutcomeLineGraphView from 'ui/features/grade_summary/backbone/views/OutcomeLineGraphView.coffee'
import OutcomeDialogView from 'ui/features/grade_summary/backbone/views/OutcomeDialogView'
import OutcomeLineGraphView from 'ui/features/grade_summary/backbone/views/OutcomeLineGraphView'
QUnit.module('OutcomeDialogViewSpec', {
setup() {

View File

@ -16,10 +16,11 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import $ from 'jquery'
import {isUndefined} from 'lodash'
import Outcome from '@canvas/grade-summary/backbone/models/Outcome.coffee'
import OutcomeResultCollection from 'ui/features/grade_summary/backbone/collections/OutcomeResultCollection.coffee'
import OutcomeLineGraphView from 'ui/features/grade_summary/backbone/views/OutcomeLineGraphView.coffee'
import OutcomeResultCollection from 'ui/features/grade_summary/backbone/collections/OutcomeResultCollection'
import OutcomeLineGraphView from 'ui/features/grade_summary/backbone/views/OutcomeLineGraphView'
import tz from '@canvas/timezone'
import fakeENV from 'helpers/fakeENV'

View File

@ -20,7 +20,7 @@ import $ from 'jquery'
import {isUndefined} from 'lodash'
import Popover from 'jquery-popover'
import Outcome from '@canvas/grade-summary/backbone/models/Outcome.coffee'
import OutcomePopoverView from 'ui/features/grade_summary/backbone/views/OutcomePopoverView.coffee'
import OutcomePopoverView from 'ui/features/grade_summary/backbone/views/OutcomePopoverView'
import template from '@canvas/outcomes/jst/outcomePopover.handlebars'
QUnit.module('OutcomePopoverViewSpec', {
@ -42,7 +42,7 @@ QUnit.module('OutcomePopoverViewSpec', {
})
test('closePopover', function () {
ok(isUndefined(this.popoverView.popover, 'precondition'))
ok(isUndefined(this.popoverView.popover), 'precondition')
ok(this.popoverView.closePopover())
this.popoverView.popover = new Popover(this.e('mouseleave'), this.popoverView.render(), {
verticalSide: 'bottom',

View File

@ -16,12 +16,13 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import $ from 'jquery'
import {isUndefined} from 'lodash'
import Outcome from '@canvas/grade-summary/backbone/models/Outcome.coffee'
import OutcomePopoverView from 'ui/features/grade_summary/backbone/views/OutcomePopoverView.coffee'
import OutcomeDialogView from 'ui/features/grade_summary/backbone/views/OutcomeDialogView.coffee'
import OutcomeView from 'ui/features/grade_summary/backbone/views/OutcomeView.coffee'
import ProgressBarView from 'ui/features/grade_summary/backbone/views/ProgressBarView.coffee'
import OutcomePopoverView from 'ui/features/grade_summary/backbone/views/OutcomePopoverView'
import OutcomeDialogView from 'ui/features/grade_summary/backbone/views/OutcomeDialogView'
import OutcomeView from 'ui/features/grade_summary/backbone/views/OutcomeView'
import ProgressBarView from 'ui/features/grade_summary/backbone/views/ProgressBarView'
import assertions from 'helpers/assertions'
QUnit.module('OutcomeViewSpec', {
@ -36,6 +37,7 @@ QUnit.module('OutcomeViewSpec', {
},
})
// eslint-disable-next-line qunit/resolve-async
test('should be accessible', function (assert) {
const done = assert.async()
assertions.isAccessible(this.outcomeView, done, {a11yReport: true})
@ -46,7 +48,7 @@ test('assign instance of ProgressBarView on init', function () {
})
test('have after render behavior', function () {
ok(isUndefined(this.outcomeView.popover, 'precondition'))
ok(isUndefined(this.outcomeView.popover), 'precondition')
this.outcomeView.render()
ok(this.outcomeView.popover instanceof OutcomePopoverView)
ok(this.outcomeView.dialog instanceof OutcomeDialogView)

View File

@ -1,57 +0,0 @@
#
# Copyright (C) 2015 - present Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
import Backbone from '@canvas/backbone'
import Outcome from '@canvas/grade-summary/backbone/models/Outcome.coffee'
import WrappedCollection from './WrappedCollection.coffee'
export default class OutcomeResultCollection extends WrappedCollection
key: 'outcome_results'
model: Outcome
@optionProperty 'outcome'
url: -> "/api/v1/courses/#{@course_id}/outcome_results?user_ids[]=#{@user_id}&outcome_ids[]=#{@outcome.id}&include[]=alignments&per_page=100"
loadAll: true
comparator: (model) ->
return -1 * model.get('submitted_or_assessed_at').getTime()
initialize: ->
super
@model = Outcome.extend defaults: {
points_possible: @outcome.get('points_possible'),
mastery_points: @outcome.get('mastery_points')
}
@course_id = ENV.context_asset_string?.replace('course_', '')
@user_id = ENV.student_id
@on('reset', @handleReset)
@on('add', @handleAdd)
handleReset: =>
@each @handleAdd
handleAdd: (model) =>
alignment_id = model.get('links').alignment
model.set('alignment_name', @alignments.get(alignment_id)?.get('name'))
if model.get('points_possible') > 0
model.set('score', model.get('points_possible') * model.get('percent'))
else
model.set('score', model.get('mastery_points') * model.get('percent'))
parse: (response) ->
@alignments ?= new Backbone.Collection([])
@alignments.add(response?.linked?.alignments || [])
response[@key]

View File

@ -0,0 +1,74 @@
//
// Copyright (C) 2015 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import Backbone from '@canvas/backbone'
import Outcome from '@canvas/grade-summary/backbone/models/Outcome.coffee'
import WrappedCollection from './WrappedCollection'
class OutcomeResultCollection extends WrappedCollection {
constructor(...args) {
super(...args)
this.handleReset = this.handleReset.bind(this)
this.handleAdd = this.handleAdd.bind(this)
}
url = () =>
`/api/v1/courses/${this.course_id}/outcome_results?user_ids[]=${this.user_id}&outcome_ids[]=${this.outcome.id}&include[]=alignments&per_page=100`
comparator = model => -1 * model.get('submitted_or_assessed_at').getTime()
initialize() {
super.initialize(...arguments)
this.model = Outcome.extend({
defaults: {
points_possible: this.outcome.get('points_possible'),
mastery_points: this.outcome.get('mastery_points'),
},
})
this.course_id = ENV.context_asset_string?.replace('course_', '')
this.user_id = ENV.student_id
this.on('reset', this.handleReset)
this.on('add', this.handleAdd)
}
handleReset = () => this.each(this.handleAdd)
handleAdd(model) {
const alignment_id = model.get('links').alignment
model.set('alignment_name', this.alignments.get(alignment_id)?.get('name'))
if (model.get('points_possible') > 0) {
model.set('score', model.get('points_possible') * model.get('percent'))
} else {
model.set('score', model.get('mastery_points') * model.get('percent'))
}
}
parse(response) {
if (this.alignments === null || typeof this.alignments === 'undefined') {
this.alignments = new Backbone.Collection([])
}
this.alignments.add(response?.linked?.alignments || [])
return response[this.key]
}
}
OutcomeResultCollection.prototype.key = 'outcome_results'
OutcomeResultCollection.prototype.model = Outcome
OutcomeResultCollection.optionProperty('outcome')
OutcomeResultCollection.prototype.loadAll = true
export default OutcomeResultCollection

View File

@ -17,13 +17,12 @@
import $ from 'jquery'
import _ from 'underscore'
import {Collection} from '@canvas/backbone'
import Section from '../models/Section.coffee'
import Group from '../models/Group.coffee'
import Section from '../models/Section'
import Group from '../models/Group'
import Outcome from '@canvas/grade-summary/backbone/models/Outcome.coffee'
import PaginatedCollection from '@canvas/pagination/backbone/collections/PaginatedCollection.coffee'
import WrappedCollection from './WrappedCollection.coffee'
import WrappedCollection from './WrappedCollection'
import natcompare from '@canvas/util/natcompare'
class GroupCollection extends PaginatedCollection {
@ -63,7 +62,7 @@ export default class OutcomeSummaryCollection extends Collection {
fetch = () => {
const dfd = $.Deferred()
const requests = _.values(this.rawCollections).map(collection => {
const requests = Object.values(this.rawCollections).map(collection => {
collection.loadAll = true
return collection.fetch()
})
@ -73,8 +72,7 @@ export default class OutcomeSummaryCollection extends Collection {
rollups() {
const studentRollups = this.rawCollections.rollups.at(0).get('scores')
const pairs = studentRollups.map(x => [x.links.outcome, x])
return _.object(pairs)
return Object.fromEntries(studentRollups.map(x => [x.links.outcome, x]))
}
populateGroupOutcomes() {

View File

@ -1,25 +0,0 @@
#
# Copyright (C) 2014 - present Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
import PaginatedCollection from '@canvas/pagination/backbone/collections/PaginatedCollection.coffee'
export default class WrappedCollection extends PaginatedCollection
@optionProperty 'key'
parse: (response) ->
@linked = response.linked
response[@key]

View File

@ -1,5 +1,5 @@
//
// Copyright (C) 2015 - present Instructure, Inc.
// Copyright (C) 2014 - present Instructure, Inc.
//
// This file is part of Canvas.
//
@ -15,19 +15,15 @@
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
// Adds _.sum method.
//
// Use like:
//
// _.sum([2,3,4]) #=> 9
//
// or with a custom accessor:
//
// _.sum([[2,3], [3,4]], (a) -> a[0]) #=> 5
import _ from 'underscore'
import PaginatedCollection from '@canvas/pagination/backbone/collections/PaginatedCollection.coffee'
export default _.mixin({
sum(array, accessor = null, start = 0) {
return _.reduce(array, (memo, el) => (accessor != null ? accessor(el) : el) + memo, start)
},
})
class WrappedCollection extends PaginatedCollection {
parse(response) {
this.linked = response.linked
return response[this.key]
}
}
WrappedCollection.optionProperty('key')
export default WrappedCollection

View File

@ -1,62 +0,0 @@
#
# Copyright (C) 2014 - present Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
import _ from 'underscore'
import {Model, Collection} from '@canvas/backbone'
import natcompare from '@canvas/util/natcompare'
export default class Group extends Model
initialize: ->
@set('outcomes', new Collection([], comparator: natcompare.byGet('friendly_name')))
count: -> @get('outcomes').length
statusCount: (status) ->
@get('outcomes').filter((x) ->
x.status() == status
).length
mastery_count: ->
@statusCount('mastery') + @statusCount('exceeds')
remedialCount: ->
@statusCount('remedial')
undefinedCount: ->
@statusCount('undefined')
status: ->
if @remedialCount() > 0
"remedial"
else
if @mastery_count() == @count()
"mastery"
else if @undefinedCount() == @count()
"undefined"
else
"near"
started: ->
true
toJSON: ->
_.extend super,
count: @count()
mastery_count: @mastery_count()
started: @started()
status: @status()

View File

@ -0,0 +1,71 @@
//
// Copyright (C) 2014 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import {Model, Collection} from '@canvas/backbone'
import natcompare from '@canvas/util/natcompare'
export default class Group extends Model {
initialize() {
return this.set('outcomes', new Collection([], {comparator: natcompare.byGet('friendly_name')}))
}
count() {
return this.get('outcomes').length
}
statusCount(status) {
return this.get('outcomes').filter(x => x.status() === status).length
}
mastery_count() {
return this.statusCount('mastery') + this.statusCount('exceeds')
}
remedialCount() {
return this.statusCount('remedial')
}
undefinedCount() {
return this.statusCount('undefined')
}
status() {
if (this.remedialCount() > 0) {
return 'remedial'
} else if (this.mastery_count() === this.count()) {
return 'mastery'
} else if (this.undefinedCount() === this.count()) {
return 'undefined'
} else {
return 'near'
}
}
started() {
return true
}
toJSON() {
return {
...super.toJSON(...arguments),
count: this.count(),
mastery_count: this.mastery_count(),
started: this.started(),
status: this.status(),
}
}
}

View File

@ -1,23 +0,0 @@
#
# Copyright (C) 2014 - present Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
import {Model, Collection} from '@canvas/backbone'
import natcompare from '@canvas/util/natcompare'
export default class Section extends Model
initialize: ->
@set('groups', new Collection([], comparator: natcompare.byGet('title')))

View File

@ -0,0 +1,25 @@
//
// Copyright (C) 2014 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import {Model, Collection} from '@canvas/backbone'
import natcompare from '@canvas/util/natcompare'
export default class Section extends Model {
initialize() {
return this.set('groups', new Collection([], {comparator: natcompare.byGet('title')}))
}
}

View File

@ -16,7 +16,7 @@
// with this program. If not, see <http://www.gnu.org/licenses/>.
import Backbone from '@canvas/backbone'
import ProgressBarView from './ProgressBarView.coffee'
import ProgressBarView from './ProgressBarView'
import template from '../../jst/alignment.handlebars'
export default class AlignmentView extends Backbone.View {

View File

@ -1,82 +0,0 @@
#
# Copyright (C) 2014 - present Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
import {useScope as useI18nScope} from '@canvas/i18n'
import {View, Collection} from '@canvas/backbone'
import _ from 'underscore'
import CollectionView from '@canvas/backbone-collection-view'
import OutcomeView from './OutcomeView.coffee'
import template from '../../jst/group.handlebars'
I18n = useI18nScope('grade_summaryGroupView')
export default class GroupView extends View
tagName: 'li'
className: 'group'
els:
'.outcomes': '$outcomes'
events:
'click .group-description': 'expand'
'keyclick .group-description': 'expand'
template: template
render: ->
super
outcomesView = new CollectionView
el: @$outcomes
collection: @model.get('outcomes')
itemView: OutcomeView
outcomesView.render()
expand: ->
@$el.toggleClass('expanded')
if @$el.hasClass("expanded")
@$el.children("div.group-description").attr("aria-expanded", "true")
else
@$el.children("div.group-description").attr("aria-expanded", "false")
$collapseToggle = $('div.outcome-toggles a.icon-collapse')
if $('li.group.expanded').length == 0
$collapseToggle.attr('disabled', 'disabled')
$collapseToggle.attr('aria-disabled', 'true')
else
$collapseToggle.removeAttr('disabled')
$collapseToggle.attr('aria-disabled', 'false')
$expandToggle = $('div.outcome-toggles a.icon-expand')
if $('li.group:not(.expanded)').length == 0
$expandToggle.attr('disabled', 'disabled')
$expandToggle.attr('aria-disabled', 'true')
else
$expandToggle.removeAttr('disabled')
$expandToggle.attr('aria-disabled', 'false')
statusTooltip: ->
switch @model.status()
when 'undefined' then I18n.t('Unstarted')
when 'remedial' then I18n.t('Well Below Mastery')
when 'near' then I18n.t('Near Mastery')
when 'mastery' then I18n.t('Meets Mastery')
when 'exceeds' then I18n.t('Exceeds Mastery')
toJSON: ->
json = super
_.extend json,
statusTooltip: @statusTooltip()

View File

@ -0,0 +1,97 @@
//
// Copyright (C) 2014 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import {useScope as useI18nScope} from '@canvas/i18n'
import $ from 'jquery'
import {View} from '@canvas/backbone'
import CollectionView from '@canvas/backbone-collection-view'
import OutcomeView from './OutcomeView'
import template from '../../jst/group.handlebars'
const I18n = useI18nScope('grade_summaryGroupView')
class GroupView extends View {
render() {
super.render(...arguments)
const outcomesView = new CollectionView({
el: this.$outcomes,
collection: this.model.get('outcomes'),
itemView: OutcomeView,
})
return outcomesView.render()
}
expand() {
this.$el.toggleClass('expanded')
if (this.$el.hasClass('expanded')) {
this.$el.children('div.group-description').attr('aria-expanded', 'true')
} else {
this.$el.children('div.group-description').attr('aria-expanded', 'false')
}
const $collapseToggle = $('div.outcome-toggles a.icon-collapse')
if ($('li.group.expanded').length === 0) {
$collapseToggle.attr('disabled', 'disabled')
$collapseToggle.attr('aria-disabled', 'true')
} else {
$collapseToggle.removeAttr('disabled')
$collapseToggle.attr('aria-disabled', 'false')
}
const $expandToggle = $('div.outcome-toggles a.icon-expand')
if ($('li.group:not(.expanded)').length === 0) {
$expandToggle.attr('disabled', 'disabled')
return $expandToggle.attr('aria-disabled', 'true')
} else {
$expandToggle.removeAttr('disabled')
return $expandToggle.attr('aria-disabled', 'false')
}
}
statusTooltip() {
switch (this.model.status()) {
case 'undefined':
return I18n.t('Unstarted')
case 'remedial':
return I18n.t('Well Below Mastery')
case 'near':
return I18n.t('Near Mastery')
case 'mastery':
return I18n.t('Meets Mastery')
case 'exceeds':
return I18n.t('Exceeds Mastery')
}
}
toJSON() {
return {
...super.toJSON(...arguments),
statusTooltip: this.statusTooltip(),
}
}
}
GroupView.prototype.template = template
GroupView.prototype.tagName = 'li'
GroupView.prototype.className = 'group'
GroupView.prototype.els = {'.outcomes': '$outcomes'}
GroupView.prototype.events = {
'click .group-description': 'expand',
'keyclick .group-description': 'expand',
}
export default GroupView

View File

@ -16,18 +16,14 @@
// with this program. If not, see <http://www.gnu.org/licenses/>.
import Backbone from '@canvas/backbone'
import OutcomeResultCollection from '../collections/OutcomeResultCollection.coffee'
import OutcomeResultCollection from '../collections/OutcomeResultCollection'
import DialogBaseView from '@canvas/dialog-base-view'
import CollectionView from '@canvas/backbone-collection-view'
import AlignmentView from './AlignmentView'
import ProgressBarView from './ProgressBarView.coffee'
import ProgressBarView from './ProgressBarView'
import template from '../../jst/outcome_detail.handlebars'
export default class OutcomeDetailView extends DialogBaseView {
static initClass() {
this.prototype.template = template
}
class OutcomeDetailView extends DialogBaseView {
dialogOptions() {
return {
containerId: 'outcome_detail',
@ -77,4 +73,7 @@ export default class OutcomeDetailView extends DialogBaseView {
return {...json, progress: this.progress}
}
}
OutcomeDetailView.initClass()
OutcomeDetailView.prototype.template = template
export default OutcomeDetailView

View File

@ -1,68 +0,0 @@
#
# Copyright (C) 2015 - present Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
import $ from 'jquery'
import _ from 'underscore'
import DialogBaseView from '@canvas/dialog-base-view'
import OutcomeLineGraphView from './OutcomeLineGraphView.coffee'
import template from '@canvas/outcomes/jst/outcomePopover.handlebars'
export default class OutcomeResultsDialogView extends DialogBaseView
@optionProperty 'model'
$target: null
template: template
initialize: ->
super
@outcomeLineGraphView = new OutcomeLineGraphView({
model: @model
})
afterRender: ->
@outcomeLineGraphView.setElement(@$("div.line-graph"))
@outcomeLineGraphView.render()
dialogOptions: ->
containerId: "outcome_results_dialog"
close: @onClose
buttons: []
width: 460
show: (e) ->
return unless (e.type == "click" || @_getKey(e.keyCode))
@$target = $(e.target)
e.preventDefault()
@$el.dialog('option', 'title', @model.get('title'))
super
@render()
onClose: =>
@$target.focus()
delete @$target
toJSON: ->
json = super
_.extend json,
dialog: true
# Private
_getKey: (keycode) =>
keys = {
13 : "enter"
32 : "spacebar"
}
keys[keycode]

View File

@ -0,0 +1,86 @@
//
// Copyright (C) 2015 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import $ from 'jquery'
import DialogBaseView from '@canvas/dialog-base-view'
import OutcomeLineGraphView from './OutcomeLineGraphView'
import template from '@canvas/outcomes/jst/outcomePopover.handlebars'
class OutcomeResultsDialogView extends DialogBaseView {
constructor(...args) {
super(...args)
this.onClose = this.onClose.bind(this)
this._getKey = this._getKey.bind(this)
}
initialize() {
super.initialize(...arguments)
return (this.outcomeLineGraphView = new OutcomeLineGraphView({
model: this.model,
}))
}
afterRender() {
this.outcomeLineGraphView.setElement(this.$('div.line-graph'))
return this.outcomeLineGraphView.render()
}
dialogOptions() {
return {
containerId: 'outcome_results_dialog',
close: this.onClose,
buttons: [],
width: 460,
}
}
show(e) {
if (e.type !== 'click' && !this._getKey(e.keyCode)) return
this.$target = $(e.target)
e.preventDefault()
this.$el.dialog('option', 'title', this.model.get('title'))
super.show(...arguments)
return this.render()
}
onClose() {
this.$target.focus()
delete this.$target
}
toJSON() {
return {
...super.toJSON(...arguments),
dialog: true,
}
}
// Private
_getKey(keycode) {
const keys = {
13: 'enter',
32: 'spacebar',
}
return keys[keycode]
}
}
OutcomeResultsDialogView.optionProperty('model')
OutcomeResultsDialogView.prototype.$target = null
OutcomeResultsDialogView.prototype.template = template
export default OutcomeResultsDialogView

View File

@ -1,285 +0,0 @@
#
# Copyright (C) 2015 - present Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
import _ from 'underscore'
import Backbone from '@canvas/backbone'
import I18n from '@canvas/i18n'
import OutcomeResultCollection from '../collections/OutcomeResultCollection.coffee'
import d3 from 'd3/d3'
import accessibleTemplate from '../../jst/accessibleLineGraph.handlebars'
import '../../sum'
# Trend class based on formulae found here:
# http://classroom.synonym.com/calculate-trendline-2709.html
class Trend
constructor: (@rawData) ->
# Returns: [[x1, y1], [x2, y2]]
data: ->
[[
@xValue(@rawData[0])
@yIntercept()
@xValue(_.last(@rawData))
(@slope() * @xValue(_.last(@rawData))) + @yIntercept()
]]
slope: ->
(@a() - @b()) / (@c() - @d())
yIntercept: ->
(@e() - @f()) / @n()
# The number of points of data.
n: ->
@rawData.length
# `n` times the sum of the products of each x & y pair.
a: ->
@n() * _.sum(@rawData, (point) => (@xValue(point) * @yValue(point)))
# The product of the sum of all x values and all y values.
b: ->
_.sum(@rawData, @xValue) * _.sum(@rawData, @yValue)
# `n` times the sum of all x values individually squared.
c: ->
@n() * _.sum(@rawData, (point) => Math.pow(@xValue(point), 2))
# The sum of all x values squared.
d: ->
Math.pow(_.sum(@rawData, @xValue), 2)
# The sum of all y values.
e: ->
_.sum(@rawData, @yValue)
# The slope times the sum of all x values.
f: ->
@slope() * _.sum(@rawData, @xValue)
xValue: (point) ->
point.x
yValue: (point) ->
point.y
export default class OutcomeLineGraphView extends Backbone.View
@optionProperty 'el'
@optionProperty 'height'
@optionProperty 'limit'
@optionProperty 'margin'
@optionProperty 'model'
@optionProperty 'timeFormat'
defaults:
height: 200
limit: 8
margin: {top: 20, right: 20, bottom: 30, left: 40}
# 2015-02-06T17:49:08Z
timeFormat: "%Y-%m-%dT%XZ"
initialize: ->
super
@deferred = $.Deferred()
@collection = new OutcomeResultCollection([], {
outcome: @model
})
@collection.on 'fetched:last', =>
@deferred.resolve()
@collection.fetch()
render: ->
if @deferred.isResolved()
return @ if @collection.isEmpty()
@_prepareScales()
@_prepareAxes()
@_prepareLines()
@svg = d3.select(@el)
.append("svg")
.attr("width", @width() + @margin.left + @margin.right)
.attr("height", @height + @margin.top + @margin.bottom)
.attr("aria-hidden", true)
.append("g")
.attr("transform", "translate(#{@margin.left}, #{@margin.top})")
@_appendAxes()
@_appendLines()
@$('.screenreader-only').append(accessibleTemplate(@toJSON()))
else
@deferred.done(@render)
@
toJSON: ->
current_user_name: ENV.current_user.display_name
data: @data()
outcome_name: @model.get('friendly_name')
# Data helpers
data: ->
@_data ?= @collection.chain()
.last(@limit)
.map((outcomeResult, i) =>
x: i
y: @percentageFor(outcomeResult.get('score'))
date: outcomeResult.get('submitted_or_assessed_at')
).value()
masteryPercentage: ->
if @model.get('points_possible') > 0
(@model.get('mastery_points') / @model.get('points_possible')) * 100
else
100
percentageFor: (score) ->
if @model.get('points_possible') > 0
((score / @model.get('points_possible')) * 100)
else
((score / @model.get('mastery_points')) * 100)
xValue: (point) =>
@x(point.x)
yValue: (point) =>
@y(point.y)
# View helpers
_appendAxes: ->
@svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0,#{@height})")
.call(@xAxis)
@svg.append("g")
.attr("class", "date-guides")
.attr("transform", "translate(0,#{@height})")
.call(@dateGuides)
@svg.append("g")
.attr("class", "y axis")
.call(@yAxis)
@svg.append("g")
.attr("class", "guides")
.call(@yGuides)
@svg.append("g")
.attr("class", "mastery-percentage-guide")
.style("stroke-dasharray", ("3, 3"))
.call(@masteryPercentageGuide)
_appendLines: ->
@svg.selectAll("circle")
.data(@data())
.enter().append("circle")
.attr("fill", "black")
.attr("r", 3)
.attr("cx", @xValue)
.attr("cy", @yValue)
@svg.append("path")
.datum(@data())
.attr("d", @line)
.attr("class", "line")
.attr("stroke", "black")
.attr("stroke-width", 1)
.attr("fill", "none")
if @trend?
@svg.selectAll(".trendline")
.data(@trend.data())
.enter()
.append("line")
.attr("class", "trendline")
.attr("x1", (d) => @x(d[0]))
.attr("y1", (d) => @y(d[1]))
.attr("x2", (d) => @x(d[2]))
.attr("y2", (d) => @y(d[3]))
.attr("stroke-width", 1)
@svg
_prepareAxes: ->
@xAxis = d3.svg.axis()
.scale(@x)
.tickFormat('')
@dateGuides = d3.svg.axis()
.scale(@xTimeScale)
.tickValues([
_.first(@data()).date
_.last(@data()).date
])
.tickFormat((d) -> Intl.DateTimeFormat(I18n.currentLocale(), { day: 'numeric', month: 'numeric'}).format(d))
@yAxis = d3.svg.axis()
.scale(@y)
.orient("left")
.tickFormat((d) -> I18n.n(d, { percentage: true }))
.tickValues([0, 50, 100])
@yGuides = d3.svg.axis()
.scale(@y)
.orient("left")
.tickValues([50, 100])
.tickSize(-@width(), 0, 0)
.tickFormat("")
@masteryPercentageGuide = d3.svg.axis()
.scale(@y)
.orient("left")
.tickValues([@masteryPercentage()])
.tickSize(-@width(), 0, 0)
.tickFormat("")
_prepareLines: ->
if @data().length >=3
@trend = new Trend(@data())
@line = d3.svg.line()
.x(@xValue)
.y(@yValue)
.interpolate('linear')
_prepareScales: ->
@x = d3.scale.linear()
.range([0, @width()])
.domain([0, @limit - 1])
@xTimeScale = d3.time.scale()
.range([0, @xTimeScaleWidth()])
.domain([
_.first(@data()).date
_.last(@data()).date
])
@y = d3.scale.linear()
.range([@height, @margin.bottom])
.domain([0, 100])
width: ->
@$el.width() - @margin.left - @margin.right - 10
# The width of the axis used to display the first and last date of scores
# displayed has to be different than the full width, in case the number
# of points is fewer than the limit (8). What we want is the width of the
# element reduced by the difference between the limit and the number of
# points we actually have, multiplied by the width each point represents,
# based on the element's width and the limit.
xTimeScaleWidth: ->
(@width() - (
(@width() / (@limit - 1)) *
(@limit - @data().length)
))

View File

@ -0,0 +1,340 @@
//
// Copyright (C) 2015 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
/*
* decaffeinate suggestions:
* DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
*/
import $ from 'jquery'
import {sumBy} from 'lodash'
import Backbone from '@canvas/backbone'
import I18n from '@canvas/i18n'
import OutcomeResultCollection from '../collections/OutcomeResultCollection'
import d3 from 'd3/d3'
import accessibleTemplate from '../../jst/accessibleLineGraph.handlebars'
const first = array => array.at(0)
const last = array => array.at(-1)
// Trend class based on formulae found here:
// http://classroom.synonym.com/calculate-trendline-2709.html
class Trend {
constructor(rawData) {
this.rawData = rawData
}
// Returns: [[x1, y1], [x2, y2]]
data() {
return [
[
this.rawData[0].x,
this.yIntercept(),
last(this.rawData).x,
this.slope() * last(this.rawData).x + this.yIntercept(),
],
]
}
slope() {
return (this.a() - this.b()) / (this.c() - this.d())
}
yIntercept() {
return (this.e() - this.f()) / this.n()
}
// The number of points of data.
n() {
return this.rawData.length
}
// `n` times the sum of the products of each x & y pair.
a() {
return this.n() * sumBy(this.rawData, p => p.x * p.y)
}
// The product of the sum of all x values and all y values.
b() {
return sumBy(this.rawData, p => p.x) * sumBy(this.rawData, p => p.y)
}
// `n` times the sum of all x values individually squared.
c() {
return this.n() * sumBy(this.rawData, p => p.x * p.x)
}
// The sum of all x values squared.
d() {
return Math.pow(
sumBy(this.rawData, p => p.x),
2
)
}
// The sum of all y values.
e() {
return sumBy(this.rawData, p => p.y)
}
// The slope times the sum of all x values.
f() {
return this.slope() * sumBy(this.rawData, p => p.x)
}
}
class OutcomeLineGraphView extends Backbone.View {
constructor(...args) {
super(...args)
this.xValue = this.xValue.bind(this)
this.yValue = this.yValue.bind(this)
}
initialize() {
super.initialize(...arguments)
this.deferred = $.Deferred()
this.collection = new OutcomeResultCollection([], {
outcome: this.model,
})
this.collection.on('fetched:last', () => {
return this.deferred.resolve()
})
return this.collection.fetch()
}
render() {
if (this.deferred.isResolved()) {
if (this.collection.isEmpty()) {
return this
}
this._prepareScales()
this._prepareAxes()
this._prepareLines()
this.svg = d3
.select(this.el)
.append('svg')
.attr('width', this.width() + this.margin.left + this.margin.right)
.attr('height', this.height + this.margin.top + this.margin.bottom)
.attr('aria-hidden', true)
.append('g')
.attr('transform', `translate(${this.margin.left}, ${this.margin.top})`)
this._appendAxes()
this._appendLines()
this.$('.screenreader-only').append(accessibleTemplate(this.toJSON()))
} else {
this.deferred.done(this.render)
}
return this
}
toJSON() {
return {
current_user_name: ENV.current_user.display_name,
data: this.data(),
outcome_name: this.model.get('friendly_name'),
}
}
// Data helpers
data() {
if (this._data === null || typeof this._data === 'undefined') {
this._data = this.collection
.chain()
.last(this.limit)
.map((outcomeResult, i) => ({
x: i,
y: this.percentageFor(outcomeResult.get('score')),
date: outcomeResult.get('submitted_or_assessed_at'),
}))
.value()
}
return this._data
}
masteryPercentage() {
if (this.model.get('points_possible') > 0) {
return (this.model.get('mastery_points') / this.model.get('points_possible')) * 100
} else {
return 100
}
}
percentageFor(score) {
if (this.model.get('points_possible') > 0) {
return (score / this.model.get('points_possible')) * 100
} else {
return (score / this.model.get('mastery_points')) * 100
}
}
xValue(point) {
return this.x(point.x)
}
yValue(point) {
return this.y(point.y)
}
// View helpers
_appendAxes() {
this.svg
.append('g')
.attr('class', 'x axis')
.attr('transform', `translate(0,${this.height})`)
.call(this.xAxis)
this.svg
.append('g')
.attr('class', 'date-guides')
.attr('transform', `translate(0,${this.height})`)
.call(this.dateGuides)
this.svg.append('g').attr('class', 'y axis').call(this.yAxis)
this.svg.append('g').attr('class', 'guides').call(this.yGuides)
return this.svg
.append('g')
.attr('class', 'mastery-percentage-guide')
.style('stroke-dasharray', '3, 3')
.call(this.masteryPercentageGuide)
}
_appendLines() {
this.svg
.selectAll('circle')
.data(this.data())
.enter()
.append('circle')
.attr('fill', 'black')
.attr('r', 3)
.attr('cx', this.xValue)
.attr('cy', this.yValue)
this.svg
.append('path')
.datum(this.data())
.attr('d', this.line)
.attr('class', 'line')
.attr('stroke', 'black')
.attr('stroke-width', 1)
.attr('fill', 'none')
if (this.trend != null) {
this.svg
.selectAll('.trendline')
.data(this.trend.data())
.enter()
.append('line')
.attr('class', 'trendline')
.attr('x1', d => this.x(d[0]))
.attr('y1', d => this.y(d[1]))
.attr('x2', d => this.x(d[2]))
.attr('y2', d => this.y(d[3]))
.attr('stroke-width', 1)
}
return this.svg
}
_prepareAxes() {
this.xAxis = d3.svg.axis().scale(this.x).tickFormat('')
this.dateGuides = d3.svg
.axis()
.scale(this.xTimeScale)
.tickValues([first(this.data()).date, last(this.data()).date])
.tickFormat(d =>
Intl.DateTimeFormat(I18n.currentLocale(), {day: 'numeric', month: 'numeric'}).format(d)
)
this.yAxis = d3.svg
.axis()
.scale(this.y)
.orient('left')
.tickFormat(d => I18n.n(d, {percentage: true}))
.tickValues([0, 50, 100])
this.yGuides = d3.svg
.axis()
.scale(this.y)
.orient('left')
.tickValues([50, 100])
.tickSize(-this.width(), 0, 0)
.tickFormat('')
return (this.masteryPercentageGuide = d3.svg
.axis()
.scale(this.y)
.orient('left')
.tickValues([this.masteryPercentage()])
.tickSize(-this.width(), 0, 0)
.tickFormat(''))
}
_prepareLines() {
if (this.data().length >= 3) {
this.trend = new Trend(this.data())
}
return (this.line = d3.svg.line().x(this.xValue).y(this.yValue).interpolate('linear'))
}
_prepareScales() {
this.x = d3.scale
.linear()
.range([0, this.width()])
.domain([0, this.limit - 1])
this.xTimeScale = d3.time
.scale()
.range([0, this.xTimeScaleWidth()])
.domain([first(this.data()).date, last(this.data()).date])
return (this.y = d3.scale.linear().range([this.height, this.margin.bottom]).domain([0, 100]))
}
width() {
return this.$el.width() - this.margin.left - this.margin.right - 10
}
// The width of the axis used to display the first and last date of scores
// displayed has to be different than the full width, in case the number
// of points is fewer than the limit (8). What we want is the width of the
// element reduced by the difference between the limit and the number of
// points we actually have, multiplied by the width each point represents,
// based on the element's width and the limit.
xTimeScaleWidth() {
return this.width() - (this.width() / (this.limit - 1)) * (this.limit - this.data().length)
}
}
OutcomeLineGraphView.optionProperty('el')
OutcomeLineGraphView.optionProperty('height')
OutcomeLineGraphView.optionProperty('limit')
OutcomeLineGraphView.optionProperty('margin')
OutcomeLineGraphView.optionProperty('model')
OutcomeLineGraphView.optionProperty('timeFormat')
OutcomeLineGraphView.prototype.defaults = {
height: 200,
limit: 8,
margin: {top: 20, right: 20, bottom: 30, left: 40},
// 2015-02-06T17:49:08Z
timeFormat: '%Y-%m-%dT%XZ',
}
export default OutcomeLineGraphView

View File

@ -1,71 +0,0 @@
#
# Copyright (C) 2015 - present Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
import Backbone from '@canvas/backbone'
import Popover from 'jquery-popover'
import OutcomeLineGraphView from './OutcomeLineGraphView.coffee'
import template from '@canvas/outcomes/jst/outcomePopover.handlebars'
export default class OutcomePopoverView extends Backbone.View
TIMEOUT_LENGTH: 50
@optionProperty 'el'
@optionProperty 'model'
events:
'click i': 'mouseleave'
'mouseenter i': 'mouseenter'
'mouseleave i': 'mouseleave'
inside: false
initialize: ->
super
@outcomeLineGraphView = new OutcomeLineGraphView({
model: @model
})
# Overrides
render: ->
template(@toJSON())
# Instance methods
closePopover: (e) ->
e?.preventDefault()
return true unless @popover?
@popover.hide()
delete @popover
mouseenter: (e) =>
@openPopover(e)
@inside = true
mouseleave: (e) =>
@inside = false
setTimeout =>
return if @inside || !@popover
@closePopover()
, @TIMEOUT_LENGTH
openPopover: (e) ->
if @closePopover()
@popover = new Popover(e, @render(), {
verticalSide: 'bottom'
manualOffset: 14
})
@outcomeLineGraphView.setElement(@popover.el.find("div.line-graph"))
@outcomeLineGraphView.render()

View File

@ -0,0 +1,95 @@
//
// Copyright (C) 2015 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
/*
* decaffeinate suggestions:
* DS206: Consider reworking classes to avoid initClass
* DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
*/
import Backbone from '@canvas/backbone'
import Popover from 'jquery-popover'
import OutcomeLineGraphView from './OutcomeLineGraphView'
import template from '@canvas/outcomes/jst/outcomePopover.handlebars'
const TIMEOUT_LENGTH = 50
class OutcomePopoverView extends Backbone.View {
constructor(...args) {
super(...args)
this.mouseenter = this.mouseenter.bind(this)
this.mouseleave = this.mouseleave.bind(this)
}
initialize() {
super.initialize(...arguments)
return (this.outcomeLineGraphView = new OutcomeLineGraphView({
model: this.model,
}))
}
// Overrides
render() {
return template(this.toJSON())
}
// Instance methods
closePopover(e) {
e?.preventDefault()
if (this.popover === null || typeof this.popover === 'undefined') return true
this.popover.hide()
return delete this.popover
}
mouseenter(e) {
this.openPopover(e)
this.inside = true
return true
}
mouseleave(_e) {
this.inside = false
setTimeout(() => {
if (!this.inside && this.popover) this.closePopover()
}, TIMEOUT_LENGTH)
}
openPopover(e) {
if (this.closePopover()) {
this.popover = new Popover(e, this.render(), {
verticalSide: 'bottom',
manualOffset: 14,
})
}
this.outcomeLineGraphView.setElement(this.popover.el.find('div.line-graph'))
return this.outcomeLineGraphView.render()
}
}
OutcomePopoverView.prototype.events = {
'click i': 'mouseleave',
'mouseenter i': 'mouseenter',
'mouseleave i': 'mouseleave',
}
OutcomePopoverView.prototype.inside = false
OutcomePopoverView.prototype.TIMEOUT_LENGTH = TIMEOUT_LENGTH
OutcomePopoverView.optionProperty('el')
OutcomePopoverView.optionProperty('model')
export default OutcomePopoverView

View File

@ -1,53 +0,0 @@
#
# Copyright (C) 2014 - present Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
import _ from 'underscore'
import Backbone from '@canvas/backbone'
import ProgressBarView from './ProgressBarView.coffee'
import OutcomePopoverView from './OutcomePopoverView.coffee'
import OutcomeDialogView from './OutcomeDialogView.coffee'
import template from '../../jst/outcome.handlebars'
export default class OutcomeView extends Backbone.View
className: 'outcome'
events:
'click .more-details' : 'show'
'keydown .more-details' : 'show'
tagName: 'li'
template: template
initialize: ->
super
@progress = new ProgressBarView(model: @model)
afterRender: ->
@popover = new OutcomePopoverView({
el: @$('.more-details')
model: @model
})
@dialog = new OutcomeDialogView({
model: @model
})
show: (e) ->
@dialog.show e
toJSON: ->
json = super
_.extend json,
progress: @progress

View File

@ -0,0 +1,60 @@
//
// Copyright (C) 2014 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import Backbone from '@canvas/backbone'
import ProgressBarView from './ProgressBarView'
import OutcomePopoverView from './OutcomePopoverView'
import OutcomeDialogView from './OutcomeDialogView'
import template from '../../jst/outcome.handlebars'
class OutcomeView extends Backbone.View {
initialize() {
super.initialize(...arguments)
return (this.progress = new ProgressBarView({model: this.model}))
}
afterRender() {
this.popover = new OutcomePopoverView({
el: this.$('.more-details'),
model: this.model,
})
return (this.dialog = new OutcomeDialogView({
model: this.model,
}))
}
show(e) {
return this.dialog.show(e)
}
toJSON() {
return {
...super.toJSON(...arguments),
progress: this.progress,
}
}
}
OutcomeView.prototype.className = 'outcome'
OutcomeView.prototype.tagName = 'li'
OutcomeView.prototype.template = template
OutcomeView.prototype.events = {
'click .more-details': 'show',
'keydown .more-details': 'show',
}
export default OutcomeView

View File

@ -1,23 +0,0 @@
#
# Copyright (C) 2014 - present Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
import Backbone from '@canvas/backbone'
import template from '../../jst/progress_bar.handlebars'
export default class ProgressBarView extends Backbone.View
className: 'bar'
template: template

View File

@ -0,0 +1,26 @@
//
// Copyright (C) 2014 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import Backbone from '@canvas/backbone'
import template from '../../jst/progress_bar.handlebars'
class ProgressBarView extends Backbone.View {}
ProgressBarView.prototype.className = 'bar'
ProgressBarView.prototype.template = template
export default ProgressBarView

View File

@ -17,7 +17,7 @@
import {View} from '@canvas/backbone'
import CollectionView from '@canvas/backbone-collection-view'
import GroupView from './GroupView.coffee'
import GroupView from './GroupView'
import template from '../../jst/section.handlebars'
export default class SectionView extends View {