Add toggling between planner screen and card screen
closes FALCOR-173 Test Plan: - Enable student planner feature flag - Go to the dashboard - The dashboard options dropdown should have Planner as an option - Changing to planner should show a "Planner placeholder" message - Refreshing the page should persist the setting Change-Id: I9062961327e03e750883246d94acd5da3283fcce Reviewed-on: https://gerrit.instructure.com/105430 Tested-by: Jenkins Reviewed-by: Stephen Jensen <sejensen@instructure.com> QA-Review: Dan Sasaki Product-Review: Colleen Palmer <colleen@instructure.com>
This commit is contained in:
parent
62db47a269
commit
9bec9774c4
|
@ -484,8 +484,10 @@ class UsersController < ApplicationController
|
|||
:DASHBOARD_SIDEBAR_URL => dashboard_sidebar_url,
|
||||
:PREFERENCES => {
|
||||
:recent_activity_dashboard => @current_user.preferences[:recent_activity_dashboard],
|
||||
:custom_colors => @current_user.custom_colors
|
||||
}
|
||||
:custom_colors => @current_user.custom_colors,
|
||||
:show_planner => show_planner?
|
||||
},
|
||||
:STUDENT_PLANNER_ENABLED => planner_enabled?
|
||||
})
|
||||
|
||||
@announcements = AccountNotification.for_user_and_account(@current_user, @domain_root_account)
|
||||
|
@ -542,6 +544,24 @@ class UsersController < ApplicationController
|
|||
render json: {}
|
||||
end
|
||||
|
||||
def dashboard_view
|
||||
if request.get?
|
||||
render json: {
|
||||
dashboard_view: @current_user.preferences[:dashboard_view]
|
||||
}
|
||||
elsif request.put?
|
||||
valid_options = ['activity', 'cards', 'planner']
|
||||
|
||||
unless valid_options.include?(params[:dashboard_view])
|
||||
return render(json: { :message => "Invalid Dashboard View Option" }, status: :bad_request)
|
||||
end
|
||||
|
||||
@current_user.preferences[:dashboard_view] = params[:dashboard_view]
|
||||
@current_user.save!
|
||||
render json: {}
|
||||
end
|
||||
end
|
||||
|
||||
include Api::V1::StreamItem
|
||||
|
||||
# @API List the activity stream
|
||||
|
|
|
@ -953,4 +953,13 @@ module ApplicationHelper
|
|||
|
||||
js_env NEW_USER_TUTORIALS: {is_enabled: is_enabled}
|
||||
end
|
||||
|
||||
def planner_enabled?
|
||||
@domain_root_account&.feature_enabled?(:student_planner)
|
||||
end
|
||||
|
||||
def show_planner?
|
||||
@current_user.preferences[:dashboard_view] == 'planner'
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -21,6 +21,14 @@ module DashboardHelper
|
|||
@current_user.preferences[:recent_activity_dashboard].present?
|
||||
end
|
||||
|
||||
def show_dashboard_cards?
|
||||
if planner_enabled?
|
||||
show_planner?
|
||||
else
|
||||
show_recent_activity?
|
||||
end
|
||||
end
|
||||
|
||||
def show_welcome_message?
|
||||
@current_user.present? && !@current_user.has_active_enrollment?
|
||||
end
|
||||
|
|
|
@ -20,7 +20,11 @@ if (ENV.DASHBOARD_SIDEBAR_URL) {
|
|||
const dashboardOptionsMenuContainer = document.getElementById('DashboardOptionsMenu_Container')
|
||||
if (dashboardOptionsMenuContainer) {
|
||||
ReactDOM.render(
|
||||
<DashboardOptionsMenu recent_activity_dashboard={ENV.PREFERENCES.recent_activity_dashboard} />,
|
||||
<DashboardOptionsMenu
|
||||
recent_activity_dashboard={ENV.PREFERENCES.recent_activity_dashboard}
|
||||
planner_enabled={ENV.STUDENT_PLANNER_ENABLED}
|
||||
planner_selected={ENV.PREFERENCES.show_planner}
|
||||
/>,
|
||||
dashboardOptionsMenuContainer
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
import ReactDOM from 'react-dom';
|
||||
import React from 'react';
|
||||
|
||||
const element = document.getElementById('dashboard-planner');
|
||||
if (element) {
|
||||
ReactDOM.render(<div>Planner placeholder</div>, element);
|
||||
}
|
|
@ -9,15 +9,27 @@ import IconSettings2Solid from 'instructure-icons/react/Solid/IconSettings2Solid
|
|||
|
||||
export default class DashboardOptionsMenu extends React.Component {
|
||||
static propTypes = {
|
||||
recent_activity_dashboard: React.PropTypes.bool.isRequired
|
||||
recent_activity_dashboard: React.PropTypes.bool.isRequired,
|
||||
planner_enabled: React.PropTypes.bool,
|
||||
planner_selected: React.PropTypes.bool
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
planner_enabled: false,
|
||||
planner_selected: false,
|
||||
}
|
||||
|
||||
constructor (props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
view: props.recent_activity_dashboard ? ['activity'] : ['cards']
|
||||
let view;
|
||||
if (props.planner_enabled) {
|
||||
view = props.planner_selected ? ['planner'] : ['cards']
|
||||
} else {
|
||||
view = props.recent_activity_dashboard ? ['activity'] : ['cards']
|
||||
}
|
||||
|
||||
this.state = { view }
|
||||
}
|
||||
|
||||
handleViewOptionSelect = (e, newSelected) => {
|
||||
|
@ -31,15 +43,26 @@ export default class DashboardOptionsMenu extends React.Component {
|
|||
}
|
||||
|
||||
toggleDashboardView () {
|
||||
const dashboardActivity = document.getElementById('dashboard-activity')
|
||||
const dashboardCards = document.getElementById('DashboardCard_Container')
|
||||
if (this.props.planner_enabled) {
|
||||
const dashboardPlanner = document.getElementById('dashboard-planner')
|
||||
dashboardPlanner.style.display = (dashboardPlanner.style.display === 'none') ? 'block' : 'none'
|
||||
} else {
|
||||
const dashboardActivity = document.getElementById('dashboard-activity')
|
||||
dashboardActivity.style.display = (dashboardActivity.style.display === 'none') ? 'block' : 'none'
|
||||
}
|
||||
|
||||
dashboardActivity.style.display = (dashboardActivity.style.display === 'none') ? 'block' : 'none'
|
||||
const dashboardCards = document.getElementById('DashboardCard_Container')
|
||||
dashboardCards.style.display = (dashboardCards.style.display === 'none') ? 'block' : 'none'
|
||||
}
|
||||
|
||||
postDashboardToggle () {
|
||||
axios.post('/users/toggle_recent_activity_dashboard')
|
||||
if (this.props.planner_enabled) {
|
||||
axios.put('/dashboard/view', {
|
||||
dashboard_view: this.state.view[0]
|
||||
})
|
||||
} else {
|
||||
axios.post('/users/toggle_recent_activity_dashboard')
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
|
@ -51,15 +74,18 @@ export default class DashboardOptionsMenu extends React.Component {
|
|||
<IconSettings2Solid />
|
||||
</Button>
|
||||
}
|
||||
contentRef={(el) => { this.menuContentRef = el; }}
|
||||
>
|
||||
<MenuItemGroup
|
||||
label={I18n.t('Dashboard View')}
|
||||
onSelect={this.handleViewOptionSelect}
|
||||
selected={this.state.view}
|
||||
>
|
||||
<MenuItem value="activity">
|
||||
{I18n.t('Recent Activity')}
|
||||
</MenuItem>
|
||||
{
|
||||
(this.props.planner_enabled) ?
|
||||
<MenuItem value="planner">{I18n.t('Planner')}</MenuItem> :
|
||||
<MenuItem value="activity">{I18n.t('Recent Activity')}</MenuItem>
|
||||
}
|
||||
<MenuItem value="cards">
|
||||
{I18n.t('Course Cards')}
|
||||
</MenuItem>
|
||||
|
|
|
@ -9,7 +9,7 @@ js_env({
|
|||
})
|
||||
%>
|
||||
|
||||
<div id="DashboardCard_Container" style="display: <%= show_recent_activity? ? 'none' : 'block' %>">
|
||||
<div id="DashboardCard_Container" style="display: <%= show_dashboard_cards? ? 'none' : 'block' %>">
|
||||
<div class="ic-DashboardCard__box">
|
||||
<% for i in 0..dashboard_courses.length-1 do %>
|
||||
<div class="ic-DashboardCard">
|
||||
|
|
|
@ -26,8 +26,14 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="dashboard-activity" class="ic-Dashboard-Activity" style="display: <%= show_recent_activity? ? 'block' : 'none' %>">
|
||||
<%= render :partial => 'shared/recent_activity' %>
|
||||
</div>
|
||||
<% if planner_enabled? %>
|
||||
<% js_bundle :student_planner %>
|
||||
<div id="dashboard-planner" class="StudentPlanner__Container" style="display: <%= show_planner? ? 'block' : 'none' %>"></div>
|
||||
<% else %>
|
||||
<div id="dashboard-activity" class="ic-Dashboard-Activity" style="display: <%= show_recent_activity? ? 'block' : 'none' %>">
|
||||
<%= render :partial => 'shared/recent_activity' %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= render :partial => 'shared/dashboard_card' %>
|
||||
</div>
|
||||
|
|
|
@ -802,6 +802,7 @@ CanvasRails::Application.routes.draw do
|
|||
end
|
||||
|
||||
scope '/dashboard' do
|
||||
put 'view' => 'users#dashboard_view'
|
||||
delete 'account_notifications/:id' => 'users#close_notification', as: :dashboard_close_notification
|
||||
get 'eportfolios' => 'eportfolios#user_index', as: :dashboard_eportfolios
|
||||
post 'comment_session' => 'services_api#start_kaltura_session', as: :dashboard_comment_session
|
||||
|
|
|
@ -1303,6 +1303,24 @@ describe UsersController do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#dashboard_view' do
|
||||
before(:each) do
|
||||
course_factory
|
||||
user_factory(active_all: true)
|
||||
user_session(@user)
|
||||
end
|
||||
|
||||
it 'sets the proper user preference on PUT requests' do
|
||||
put :dashboard_view, :dashboard_view => 'cards'
|
||||
expect(@user.preferences[:dashboard_view]).to eql('cards')
|
||||
end
|
||||
|
||||
it 'does not allow arbitrary values to be set' do
|
||||
put :dashboard_view, :dashboard_view => 'a non-whitelisted value'
|
||||
assert_status(400)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#invite_users" do
|
||||
it 'does not work without ability to manage students or admins on course' do
|
||||
Account.default.tap{|a| a.settings[:open_registration] = true; a.save!}
|
||||
|
|
|
@ -780,4 +780,42 @@ describe ApplicationHelper do
|
|||
end
|
||||
end
|
||||
|
||||
describe "planner_enabled?" do
|
||||
before(:each) do
|
||||
@domain_root_account = Account.default
|
||||
end
|
||||
|
||||
context "with student_planner feature flag enabled" do
|
||||
before(:each) do
|
||||
@domain_root_account.enable_feature! :student_planner
|
||||
end
|
||||
|
||||
it "return true" do
|
||||
expect(planner_enabled?).to be true
|
||||
end
|
||||
end
|
||||
|
||||
context "with student_planner feature flag disabled" do
|
||||
it "returns false" do
|
||||
expect(planner_enabled?).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "show_planner?" do
|
||||
before(:each) do
|
||||
@current_user = User.create!
|
||||
end
|
||||
|
||||
it "returns true when the dashboard view is set to planner" do
|
||||
@current_user.preferences[:dashboard_view] = 'planner'
|
||||
expect(show_planner?).to be true
|
||||
end
|
||||
|
||||
it "returns false when the dashboard view is not set to planner" do
|
||||
@current_user.preferences[:dashboard_view] = 'cards'
|
||||
expect(show_planner?).to be false
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -20,7 +20,7 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
|||
|
||||
describe DashboardHelper do
|
||||
include DashboardHelper
|
||||
|
||||
|
||||
context "show_welcome_message?" do
|
||||
it "should be true if the user has no current enrollments" do
|
||||
user_model
|
||||
|
|
|
@ -3,30 +3,48 @@ import ReactDOM from 'react-dom'
|
|||
import TestUtils from 'react-addons-test-utils'
|
||||
import { mount } from 'enzyme'
|
||||
import DashboardOptionsMenu from 'jsx/dashboard_card/DashboardOptionsMenu'
|
||||
import moxios from 'moxios';
|
||||
|
||||
const container = document.getElementById('fixtures')
|
||||
|
||||
const FakeDashboard = function ({menuRef}) {
|
||||
const FakeDashboard = function (props) {
|
||||
return (
|
||||
<div>
|
||||
<DashboardOptionsMenu
|
||||
ref={(c) => { menuRef(c) }}
|
||||
ref={(c) => { props.menuRef(c) }}
|
||||
recent_activity_dashboard
|
||||
{...props}
|
||||
/>
|
||||
<div
|
||||
id="dashboard-activity"
|
||||
style={{ display: 'block' }}
|
||||
/>
|
||||
{
|
||||
(props.planner_enabled) ? (
|
||||
<div
|
||||
id="dashboard-planner"
|
||||
style={{ display: 'block' }}
|
||||
/>
|
||||
) :
|
||||
(
|
||||
<div
|
||||
id="dashboard-activity"
|
||||
style={{ display: 'block' }}
|
||||
/>
|
||||
)
|
||||
}
|
||||
<div
|
||||
id="DashboardCard_Container"
|
||||
style={{ display: 'none' }}
|
||||
/>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
FakeDashboard.propTypes = {
|
||||
menuRef: React.PropTypes.func.isRequired
|
||||
menuRef: React.PropTypes.func.isRequired,
|
||||
planner_enabled: React.PropTypes.bool
|
||||
}
|
||||
|
||||
FakeDashboard.defaultProps = {
|
||||
planner_enabled: false
|
||||
}
|
||||
|
||||
QUnit.module('Dashboard Options Menu', {
|
||||
|
@ -73,3 +91,57 @@ test('it should not call toggleDashboardView when correct view is already set',
|
|||
equal(toggleDashboardView.callCount, 0)
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
test('it should switch dashboard view appropriately with Student Planner enabled when view option is selected', function () {
|
||||
this.stub(DashboardOptionsMenu.prototype, 'postDashboardToggle')
|
||||
|
||||
let dashboardMenu = null
|
||||
ReactDOM.render(
|
||||
<FakeDashboard
|
||||
menuRef={(c) => { dashboardMenu = c }}
|
||||
planner_enabled
|
||||
/>, container)
|
||||
|
||||
dashboardMenu.handleViewOptionSelect(null, ['cards'])
|
||||
ok(document.getElementById('dashboard-planner').style.display === 'none')
|
||||
ok(document.getElementById('DashboardCard_Container').style.display === 'block')
|
||||
|
||||
dashboardMenu.handleViewOptionSelect(null, ['planner'])
|
||||
ok(document.getElementById('dashboard-planner').style.display === 'block')
|
||||
ok(document.getElementById('DashboardCard_Container').style.display === 'none')
|
||||
})
|
||||
|
||||
test('it should use the dashboard view endpoint when Student Planner is enabled', function (assert) {
|
||||
const done = assert.async();
|
||||
moxios.install();
|
||||
let dashboardMenu = null
|
||||
ReactDOM.render(
|
||||
<FakeDashboard
|
||||
menuRef={(c) => { dashboardMenu = c }}
|
||||
planner_enabled
|
||||
/>, container)
|
||||
|
||||
dashboardMenu.handleViewOptionSelect(null, ['cards'])
|
||||
dashboardMenu.postDashboardToggle();
|
||||
|
||||
moxios.wait(() => {
|
||||
const request = moxios.requests.mostRecent()
|
||||
equal(request.url, '/dashboard/view')
|
||||
equal(request.config.data, '{"dashboard_view":"cards"}');
|
||||
done()
|
||||
})
|
||||
|
||||
moxios.uninstall();
|
||||
});
|
||||
|
||||
test('it should include a planner menu item when Student Planner is enabled', function () {
|
||||
const wrapper = mount(
|
||||
<DashboardOptionsMenu
|
||||
recent_activity_dashboard={false}
|
||||
planner_enabled
|
||||
/>
|
||||
)
|
||||
wrapper.find('button').simulate('click')
|
||||
const menuItems = Array.from(document.querySelectorAll('[role="menuitemradio"]'))
|
||||
ok(menuItems.some(menuItem => menuItem.textContent === 'Planner'))
|
||||
});
|
||||
|
|
|
@ -49,4 +49,21 @@ describe "/users/user_dashboard" do
|
|||
render "users/user_dashboard"
|
||||
expect(response.body).to match /My Global Announcement/
|
||||
end
|
||||
|
||||
context "with Student Planner enabled" do
|
||||
it "should render out a div with id of dashboard-planner" do
|
||||
course_with_student
|
||||
view_context
|
||||
@course.account.enable_feature!(:student_planner)
|
||||
assign(:courses, [@course])
|
||||
assign(:enrollments, [@enrollment])
|
||||
assign(:group_memberships, [])
|
||||
assign(:topics, [])
|
||||
assign(:upcoming_events, [])
|
||||
assign(:stream_items, [])
|
||||
|
||||
render "users/user_dashboard"
|
||||
expect(response.body).to match /id="dashboard-planner"/
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue