-
Notifications
You must be signed in to change notification settings - Fork 177
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Vueified channel list page #1264
Merged
rtibbles
merged 44 commits into
learningequality:develop
from
jayoshih:vue-channel-list
Mar 22, 2019
Merged
Changes from all commits
Commits
Show all changes
44 commits
Select commit
Hold shift + click to select a range
2711579
Got channel list to load and set active channels
jayoshih 7399419
Added starring functions
jayoshih cde4d59
Added channel set rendering
jayoshih 1f503a1
Added invitations
jayoshih 21231e0
Added opening channel set modal
jayoshih 8b155c3
Use rest framework reponse on channel accept
jayoshih b6db5f0
Started adding channel details
jayoshih 5c1d933
Got details panel to work
jayoshih ac3ef06
Added channel detail exporting
jayoshih e0d4fcb
Clearned up store
jayoshih 0669014
Removed unused functions
jayoshih c6770f2
Updated styling
jayoshih 58f3b3e
Separated out modules
jayoshih 612cad4
Separated out shared components
jayoshih c46351e
Removed legacy files
jayoshih b406374
Added tab mixin to unify how tabs are handled across components
jayoshih 237ba7a
Merge branch 'develop' of https://github.com/learningequality/studio …
jayoshih cfb93c0
Fixed is_channel logic on details panel
jayoshih 76a04aa
Removed commented out code, updated changelog
jayoshih 18b03b3
Added tests for views
jayoshih 2bca790
Finished tests
jayoshih 9228d95
Fixed store test
jayoshih fb83a08
Added translations to vue
jayoshih 2376b17
Added vue mock
jayoshih 80a0e91
Fixed channel sort order
jayoshih bc0c671
Adding key for linting rules.
DXCanas 87575c1
Modifying channel editor to be a form.
DXCanas 16ee4fe
ChannelEditor css cleanup.
DXCanas c95a38f
Having styles match Channel Details a little better.
DXCanas 89cfe84
Adding webpack aliases to eslint rules.
DXCanas 0167a12
Merge pull request #33 from DXCanas/vue-channels-list
beb946d
Merge branch 'develop' into vue-channel-list
9b87f3d
Merge branch 'develop' into vue-channel-list
7b6d78f
Merge branch 'develop' of https://github.com/learningequality/studio …
jayoshih 9a41506
Merge branch 'vue-channel-list' of https://github.com/jayoshih/conten…
jayoshih d997097
Made smaller fixes
jayoshih e2c26ec
Added prop validator
jayoshih 7a4b7c5
Updated test
jayoshih 571652e
Fixed test
jayoshih 44811d9
Moved preferences to shared vue state
jayoshih 397c6ab
Switched to object spread
jayoshih b55654f
Removed backbone wrapper
jayoshih 79aa6f4
Pass ids instead of objects into components and actions
jayoshih 04fea11
Fixed tests
jayoshih File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import Vue from 'vue'; | ||
var vueIntl = require("vue-intl"); | ||
|
||
// Just copy/pasted code from utils/translations.js here | ||
// as trying to import the actual file led to some errors | ||
|
||
var translations = window.ALL_MESSAGES || {}; // Set in django | ||
|
||
// Flatten translation dictionary | ||
var unnested_translations = {}; | ||
Object.keys(translations).forEach(function (key) { | ||
Object.keys(translations[key]).forEach(function(nestedKey) { | ||
unnested_translations[key + "." + nestedKey] = translations[key][nestedKey]; | ||
}); | ||
}); | ||
|
||
Vue.use(vueIntl, {"defaultLocale": "en"}); | ||
|
||
var currentLanguage = "en"; | ||
if (global.languageCode) { | ||
currentLanguage = global.languageCode; | ||
Vue.setLocale(currentLanguage); | ||
} | ||
|
||
Vue.registerMessages(currentLanguage, unnested_translations); | ||
Vue.prototype.$tr = function $tr(messageId, args) { | ||
const nameSpace = this.$options.name; | ||
if (args) { | ||
if (!Array.isArray(args) && typeof args !== 'object') { | ||
logging.error(`The $tr functions take either an array of positional | ||
arguments or an object of named options.`); | ||
} | ||
} | ||
const defaultMessageText = this.$options.$trs[messageId]; | ||
const message = { | ||
id: `${nameSpace}.${messageId}`, | ||
defaultMessage: defaultMessageText, | ||
}; | ||
|
||
return this.$formatMessage(message, args); | ||
}; | ||
|
||
module.exports = Vue; |
19 changes: 19 additions & 0 deletions
19
contentcuration/contentcuration/static/js/edit_channel/channel_list/constants.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
export const ListTypes = { | ||
EDITABLE: 'EDITABLE', | ||
STARRED: 'STARRED', | ||
VIEW_ONLY: 'VIEW_ONLY', | ||
PUBLIC: 'PUBLIC', | ||
CHANNEL_SETS: 'CHANNEL_SETS' | ||
}; | ||
|
||
export const ChannelListUrls = { | ||
[ListTypes.EDITABLE]: window.Urls.get_user_edit_channels(), | ||
[ListTypes.STARRED]: window.Urls.get_user_bookmarked_channels(), | ||
[ListTypes.VIEW_ONLY]: window.Urls.get_user_view_channels(), | ||
[ListTypes.PUBLIC]: window.Urls.get_user_public_channels() | ||
} | ||
|
||
export const ChannelInvitationMapping = { | ||
'edit': ListTypes.EDITABLE, | ||
'view': ListTypes.VIEW_ONLY | ||
} |
59 changes: 59 additions & 0 deletions
59
contentcuration/contentcuration/static/js/edit_channel/channel_list/mixins.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import _ from 'underscore'; | ||
import { mapGetters, mapActions, mapMutations, mapState } from 'vuex'; | ||
import { createTranslator } from 'utils/i18n'; | ||
import { dialog, alert } from 'edit_channel/utils/dialog'; | ||
|
||
const channelStrings = createTranslator('ChannelStrings', { | ||
unsavedChanges: "Unsaved Changes!", | ||
unsavedChangesText: "Exiting now will undo any new changes. Are you sure you want to exit?", | ||
dontSave: "Discard Changes", | ||
keepOpen: "Keep Editing", | ||
save: "Save", | ||
errorChannelSave: "Error Saving Channel" | ||
}); | ||
|
||
exports.setChannelMixin = { | ||
computed: { | ||
...mapState('channel_list', ['activeChannel']), | ||
...mapState('channel_list', ['changed']), | ||
channelStrings() { | ||
return channelStrings; | ||
} | ||
}, | ||
methods: { | ||
...mapActions('channel_list', ['saveChannel']), | ||
...mapMutations('channel_list', { | ||
setActiveChannel: 'SET_ACTIVE_CHANNEL', | ||
cancelChanges: 'CANCEL_CHANNEL_CHANGES' | ||
}), | ||
setChannel: function (channelID) { | ||
// Check for changes here when user switches or closes panel | ||
if(this.changed && channelID !== this.activeChannel.id) { | ||
dialog(this.channelStrings("unsavedChanges"), this.channelStrings("unsavedChangesText"), { | ||
[this.channelStrings("dontSave")]: () => { | ||
this.cancelChanges(); | ||
this.setActiveChannel(channelID); | ||
}, | ||
[this.channelStrings("keepOpen")]:() => {}, | ||
[this.channelStrings("save")]: () => { | ||
this.saveChannel().then(() => { | ||
this.setActiveChannel(channelID); | ||
}).catch( (error) => { | ||
alert(this.channelStrings('errorChannelSave'), error.responseText || error); | ||
}); | ||
}, | ||
}, null); | ||
} else { | ||
this.setActiveChannel(channelID); | ||
} | ||
} | ||
} | ||
}; | ||
|
||
exports.tabMixin = { | ||
mounted() { | ||
this.$nextTick(() => { | ||
this.$refs.firstTab.focus(); | ||
}); | ||
} | ||
}; |
27 changes: 27 additions & 0 deletions
27
...on/contentcuration/static/js/edit_channel/channel_list/tests/channelDeleteSection.spec.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import { mount } from '@vue/test-utils'; | ||
import ChannelDeleteSection from './../views/ChannelDeleteSection.vue'; | ||
import _ from 'underscore'; | ||
import { localStore, mockFunctions } from './data.js'; | ||
|
||
|
||
function makeWrapper(props = {}) { | ||
return mount(ChannelDeleteSection, { | ||
store: localStore | ||
}) | ||
} | ||
|
||
describe('channelDeleteSection', () => { | ||
let wrapper; | ||
beforeEach(() => { | ||
wrapper = makeWrapper(); | ||
}); | ||
|
||
it('clicking delete channel should open a dialog warning', () => { | ||
wrapper.find('.delete-channel').trigger('click'); | ||
expect(mockFunctions.deleteChannel).not.toHaveBeenCalled(); | ||
|
||
expect(document.querySelector('#dialog-box')).toBeTruthy(); | ||
wrapper.vm.deleteChannel(wrapper.channel); | ||
expect(mockFunctions.deleteChannel).toHaveBeenCalled(); | ||
}); | ||
}); |
33 changes: 33 additions & 0 deletions
33
...ion/contentcuration/static/js/edit_channel/channel_list/tests/channelDetailsPanel.spec.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { shallowMount } from '@vue/test-utils'; | ||
import ChannelDetailsPanel from './../views/ChannelDetailsPanel.vue'; | ||
import _ from 'underscore'; | ||
import { localStore, mockFunctions } from './data.js'; | ||
|
||
|
||
function makeWrapper(props = {}) { | ||
let channel = { | ||
id: 'test', | ||
main_tree: "abc", | ||
...props | ||
} | ||
localStore.commit('channel_list/RESET_STATE', channel) | ||
localStore.commit('channel_list/ADD_CHANNEL', channel) | ||
localStore.commit('channel_list/SET_ACTIVE_CHANNEL', channel.id) | ||
return shallowMount(ChannelDetailsPanel, { | ||
store: localStore | ||
}) | ||
} | ||
|
||
describe('channelDetailsPanel', () => { | ||
it('panel should set background as thumbnail', () => { | ||
let wrapper = makeWrapper({'thumbnail_url': 'test.png'}); | ||
let panel = wrapper.find('#channel-preview-wrapper'); | ||
let expectedStyle = "background-image: url('test.png')"; | ||
expect(panel.attributes('style')).toContain('test.png'); | ||
}); | ||
it('panel should set background as default thumbnail for new channels', () => { | ||
let wrapper = makeWrapper(); | ||
let panel = wrapper.find('#channel-preview-wrapper'); | ||
expect(panel.attributes('style')).toContain('kolibri_placeholder.png'); | ||
}); | ||
}); |
41 changes: 41 additions & 0 deletions
41
...contentcuration/static/js/edit_channel/channel_list/tests/channelDownloadDropdown.spec.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import { mount } from '@vue/test-utils'; | ||
import ChannelDownloadDropdown from './../views/ChannelDownloadDropdown.vue'; | ||
import _ from 'underscore'; | ||
import { localStore, mockFunctions } from './data.js'; | ||
|
||
|
||
function makeWrapper(props = {}) { | ||
return mount(ChannelDownloadDropdown, { | ||
store: localStore | ||
}) | ||
} | ||
|
||
const testChannel = { id: 'test', name: 'test' } | ||
localStore.commit('channel_list/ADD_CHANNEL', testChannel) | ||
|
||
|
||
describe('channelDownloadDropdown', () => { | ||
let wrapper; | ||
beforeEach(() => { | ||
localStore.commit('channel_list/SET_ACTIVE_CHANNEL', testChannel.id); | ||
mockFunctions.downloadChannelDetails.mockReset(); | ||
wrapper = makeWrapper(); | ||
}); | ||
|
||
it('clicking a download link should trigger a download', () => { | ||
wrapper.find('li a').trigger('click'); | ||
expect(mockFunctions.downloadChannelDetails).toHaveBeenCalled(); | ||
}); | ||
it('requesting a format should trigger corresponding format download', () => { | ||
function test(format) { | ||
wrapper.vm.downloadDetails(format); | ||
let args = mockFunctions.downloadChannelDetails.mock.calls[0][1]; | ||
expect(args.format).toEqual(format); | ||
mockFunctions.downloadChannelDetails.mockReset(); | ||
} | ||
test('pdf'); | ||
test('csv'); | ||
test('detailedPdf'); | ||
test('ppt'); | ||
}); | ||
}); |
122 changes: 122 additions & 0 deletions
122
...tcuration/contentcuration/static/js/edit_channel/channel_list/tests/channelEditor.spec.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
import { mount } from '@vue/test-utils'; | ||
import ChannelEditor from './../views/ChannelEditor.vue'; | ||
import { localStore, mockFunctions } from './data.js'; | ||
require('handlebars/helpers'); // Needed for image uploader | ||
|
||
let testChannel = { | ||
'id': 'test', | ||
'name': 'channel', | ||
'description': "description", | ||
'language': 'en', | ||
} | ||
|
||
function makeWrapper(props = {}) { | ||
let channel = { | ||
...testChannel, | ||
...props | ||
}; | ||
localStore.commit('channel_list/RESET_STATE'); | ||
localStore.commit('channel_list/ADD_CHANNEL', channel); | ||
localStore.commit('channel_list/SET_ACTIVE_CHANNEL', channel.id); | ||
return mount(ChannelEditor, { | ||
store: localStore | ||
}); | ||
} | ||
|
||
describe('channelEditor', () => { | ||
let wrapper; | ||
beforeEach(() => { | ||
wrapper = makeWrapper(); | ||
}); | ||
|
||
describe('on load', () => { | ||
it('changed is false', () => { | ||
expect(wrapper.vm.changed).toBe(false); | ||
}); | ||
it('thumbnail is shown', () => { | ||
let thumbnail = "thumbnail.png"; | ||
let thumbnailWrapper = makeWrapper({'thumbnail_url': thumbnail}); | ||
expect(thumbnailWrapper.find('img').attributes('src')).toContain(thumbnail); | ||
}); | ||
it('default thumbnail is shown if channel has no thumbnail', () => { | ||
expect(wrapper.find('img').attributes('src')).toContain('kolibri_placeholder.png') | ||
}); | ||
it('.channel-name is set to channel.name', () => { | ||
expect(wrapper.find('.channel-name').element.value).toEqual(testChannel.name); | ||
}); | ||
it('.channel-description is set to channel.description', () => { | ||
expect(wrapper.find('.channel-description').element.value).toEqual(testChannel.description); | ||
}); | ||
it('#select-language is set to channel.language_id', () => { | ||
expect(wrapper.find('#select-language').element.value).toEqual(testChannel.language); | ||
}); | ||
}); | ||
|
||
describe('changes registered', () => { | ||
beforeEach(() => { | ||
localStore.commit('channel_list/SET_CHANGED', false); | ||
}); | ||
it('setChannelThumbnail sets channel.thumbnail', () => { | ||
let thumbnail = 'newthumbnail.png' | ||
wrapper.vm.setChannelThumbnail(thumbnail, {'new encoding': thumbnail}, thumbnail, thumbnail) | ||
expect(wrapper.vm.changed).toBe(true); | ||
expect(wrapper.vm.channel.thumbnail).toEqual(thumbnail); | ||
expect(wrapper.vm.channel.thumbnail_encoding).toEqual({'new encoding': thumbnail}); | ||
}); | ||
it('removeChannelThumbnail removes channel.thumbnail', () => { | ||
let thumbnailWrapper = makeWrapper({'thumbnail': 'thumbnail.png', 'thumbnail_encoding': {'test': 'test'}}) | ||
thumbnailWrapper.vm.removeChannelThumbnail(); | ||
expect(wrapper.vm.changed).toBe(true); | ||
expect(wrapper.vm.channel.thumbnail).toEqual(""); | ||
expect(wrapper.vm.channel.thumbnail_encoding).toEqual({}); | ||
}); | ||
it('typing in .channel-name sets channel.name', () => { | ||
let newName = 'new channel name'; | ||
let nameInput = wrapper.find('.channel-name'); | ||
nameInput.element.value = newName; | ||
nameInput.trigger('input'); | ||
nameInput.trigger('blur'); | ||
expect(wrapper.vm.changed).toBe(true); | ||
expect(wrapper.vm.channel.name).toEqual(newName); | ||
}); | ||
it('setting .channel-description sets channel.description', () => { | ||
let newDescription = "new channel description"; | ||
let descriptionInput = wrapper.find('.channel-description'); | ||
descriptionInput.element.value = newDescription; | ||
descriptionInput.trigger('input'); | ||
descriptionInput.trigger('blur'); | ||
expect(wrapper.vm.changed).toBe(true); | ||
expect(wrapper.vm.channel.description).toEqual(newDescription); | ||
}); | ||
it('setting #select-language sets channel.language_id', () => { | ||
let newLanguage = "en"; | ||
let languageDropdown = wrapper.find('#select-language'); | ||
languageDropdown.element.value = newLanguage; | ||
languageDropdown.trigger('input'); | ||
languageDropdown.trigger('blur'); | ||
expect(wrapper.vm.changed).toBe(true); | ||
expect(wrapper.vm.channel.language).toEqual(newLanguage); | ||
}); | ||
}) | ||
|
||
it('clicking CANCEL cancels edits', () => { | ||
let nameInput = wrapper.find('.channel-name'); | ||
nameInput.element.value = 'new channel name'; | ||
nameInput.trigger('input'); | ||
nameInput.trigger('blur'); | ||
|
||
// Cancel changes | ||
wrapper.find(".cancel-edits").trigger('click'); | ||
expect(wrapper.vm.changed).toBe(false); | ||
expect(wrapper.vm.channel.name).toEqual('channel'); | ||
expect(wrapper.emitted().cancelEdit).toBeTruthy(); | ||
}); | ||
|
||
it('clicking SAVE saves edits', () => { | ||
wrapper.find('form').trigger('submit'); | ||
expect(mockFunctions.saveChannel).toHaveBeenCalled(); | ||
wrapper.vm.$nextTick(() => { | ||
expect(wrapper.emitted('submitChanges')).toBeTruthy(); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This pattern has cropped up on Kolibri too - seems like it is probably worth my while thinking about how to make
$trs
work for mixins.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would be great!