Skip to content

Commit

Permalink
Merge pull request #1264 from jayoshih/vue-channel-list
Browse files Browse the repository at this point in the history
Vueified channel list page
  • Loading branch information
rtibbles authored Mar 22, 2019
2 parents 1dee892 + 04fea11 commit 6dad17d
Show file tree
Hide file tree
Showing 74 changed files with 4,088 additions and 2,073 deletions.
3 changes: 3 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ rules:
vue/v-bind-style: 2
vue/v-on-style: 2
vue/html-quotes: [2, 'double']
require-path-exists/exists:
- 2
- webpackConfigPath: webpack.config.js
env:
browser: true
commonjs: true
Expand Down
8 changes: 6 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@
## Upcoming release
#### Changes
* [[@jayoshih](https://github.com/jayoshih)] Updated README
*
* [[@jayoshih](https://github.com/jayoshih)] Vueified channel list page

#### Issues Resolved
* [#875 Make sure files referenced by both orphan and non-orphan nodes aren't deleted](https://github.com/learningequality/studio/issues/875)
*
* [#1120](https://github.com/learningequality/studio/issues/1120)
* [#900](https://github.com/learningequality/studio/issues/900)
* [#875 Make sure files referenced by both orphan and non-orphan nodes aren't deleted](https://github.com/learningequality/studio/issues/875)



## 2019-02-11 Release
#### Changes
Expand Down
43 changes: 43 additions & 0 deletions __mocks__/vue.js
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;
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
}
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();
});
}
};
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();
});
});
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');
});
});
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');
});
});
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();
});
});
});
Loading

0 comments on commit 6dad17d

Please sign in to comment.