Skip to content

Commit 434f375

Browse files
authored
Merge pull request #5513 from taoerman/submission-side-panel
Info messages about previous submissions in create submission side panel
2 parents 157c3fe + f8ea41b commit 434f375

File tree

5 files changed

+430
-179
lines changed

5 files changed

+430
-179
lines changed

contentcuration/contentcuration/frontend/channelEdit/components/sidePanels/SubmitToCommunityLibrarySidePanel/Box.vue

Lines changed: 76 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,26 @@
1515
class="box-content"
1616
>
1717
<div class="box-icon">
18-
<KIcon :icon="icon" />
18+
<KIcon
19+
:icon="icon"
20+
:style="{ fontSize: '18px' }"
21+
/>
22+
</div>
23+
<div class="box-text">
24+
<div
25+
v-if="$slots.title || title"
26+
class="box-title"
27+
>
28+
<slot name="title">{{ title }}</slot>
29+
</div>
30+
<div
31+
v-if="$slots.description || description"
32+
class="box-description"
33+
>
34+
<slot name="description">{{ description }}</slot>
35+
</div>
36+
<slot></slot>
1937
</div>
20-
<slot></slot>
2138
<div
2239
v-if="$slots.chip"
2340
class="chip"
@@ -45,37 +62,47 @@
4562
const boxBackgroundColor = computed(() => {
4663
switch (props.kind) {
4764
case 'warning':
48-
return paletteTheme.yellow.v_100;
65+
return paletteTheme.red.v_100;
4966
case 'info':
5067
return paletteTheme.grey.v_100;
5168
default:
5269
throw new Error(`Unsupported box kind: ${props.kind}`);
5370
}
5471
});
55-
const boxTextColor = computed(() => {
72+
const boxBorderColor = computed(() => {
5673
switch (props.kind) {
5774
case 'warning':
58-
return paletteTheme.red.v_500;
75+
return paletteTheme.red.v_300;
5976
case 'info':
60-
return tokensTheme.text;
77+
return 'transparent';
6178
default:
62-
throw new Error(`Unsupported box kind: ${props.kind}`);
79+
return 'transparent';
6380
}
6481
});
6582
const icon = computed(() => {
6683
switch (props.kind) {
6784
case 'warning':
68-
return 'warningIncomplete';
85+
return 'error';
6986
case 'info':
7087
return 'infoOutline';
7188
default:
7289
throw new Error(`Unsupported box kind: ${props.kind}`);
7390
}
7491
});
7592
93+
const titleColor = computed(() => {
94+
return props.kind === 'warning' ? paletteTheme.red.v_600 : tokensTheme.text;
95+
});
96+
97+
const descriptionColor = computed(() => {
98+
return props.kind === 'warning' ? paletteTheme.grey.v_800 : tokensTheme.text;
99+
});
100+
76101
return {
77102
boxBackgroundColor,
78-
boxTextColor,
103+
boxBorderColor,
104+
titleColor,
105+
descriptionColor,
79106
icon,
80107
};
81108
},
@@ -91,6 +118,16 @@
91118
required: false,
92119
default: false,
93120
},
121+
title: {
122+
type: String,
123+
required: false,
124+
default: '',
125+
},
126+
description: {
127+
type: String,
128+
required: false,
129+
default: '',
130+
},
94131
},
95132
};
96133
@@ -100,20 +137,46 @@
100137
<style lang="scss" scoped>
101138
102139
.box {
103-
padding: 8px;
104-
color: v-bind('boxTextColor');
140+
padding: 10px;
105141
background-color: v-bind('boxBackgroundColor');
142+
border: 1px solid v-bind('boxBorderColor');
106143
border-radius: 4px;
107144
}
108145
109146
.box-content {
110147
display: flex;
111148
gap: 8px;
149+
align-items: start;
112150
}
113151
114152
.box-icon {
115-
width: 20px;
116-
height: 20px;
153+
display: flex;
154+
flex-shrink: 0;
155+
align-items: center;
156+
justify-content: center;
157+
width: 18px;
158+
height: 18px;
159+
font-size: 18px;
160+
line-height: 1;
161+
}
162+
163+
.box-text {
164+
display: flex;
165+
flex: 1;
166+
flex-direction: column;
167+
gap: 4px;
168+
font-size: 14px;
169+
line-height: 140%;
170+
}
171+
172+
.box-title {
173+
font-weight: 600;
174+
color: v-bind('titleColor');
175+
}
176+
177+
.box-description {
178+
font-weight: 400;
179+
color: v-bind('descriptionColor');
117180
}
118181
119182
.chip {

contentcuration/contentcuration/frontend/channelEdit/components/sidePanels/SubmitToCommunityLibrarySidePanel/__tests__/SubmitToCommunityLibrarySidePanel.spec.js

Lines changed: 39 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,16 @@ jest.mock('shared/data/resources', () => ({
2727

2828
const store = factory();
2929

30-
const {
31-
nonePrimaryInfo$,
32-
flaggedPrimaryInfo$,
33-
approvedPrimaryInfo$,
34-
submittedPrimaryInfo$,
35-
reviewersWillSeeLatestFirst$,
36-
} = communityChannelsStrings;
30+
const { nonePrimaryInfo$, flaggedPrimaryInfo$, approvedPrimaryInfo$, submittedPrimaryInfo$ } =
31+
communityChannelsStrings;
3732

3833
async function makeWrapper({ channel, publishedData, latestSubmission }) {
3934
const isLoading = ref(true);
4035
const isFinished = ref(false);
4136

37+
store.state.currentChannel.currentChannelId = channel.id;
38+
store.commit('channel/ADD_CHANNEL', channel);
39+
4240
usePublishedData.mockReturnValue({
4341
isLoading,
4442
isFinished,
@@ -79,9 +77,9 @@ const publishedNonPublicChannel = {
7977
};
8078

8179
const publicChannel = {
82-
id: 'published-non-public-channel',
80+
id: 'public-channel',
8381
version: 2,
84-
name: 'Published Non-Public Channel',
82+
name: 'Public Channel',
8583
published: true,
8684
public: true,
8785
};
@@ -110,6 +108,10 @@ const publishedData = {
110108
const submittedLatestSubmission = { channel_version: 2, status: CommunityLibraryStatus.PENDING };
111109

112110
describe('SubmitToCommunityLibrarySidePanel', () => {
111+
beforeEach(() => {
112+
store.state.currentChannel.currentChannelId = null;
113+
store.state.channel.channelsMap = {};
114+
});
113115
describe('correct warnings are shown', () => {
114116
it('when channel is published, not public and not submitted', async () => {
115117
const wrapper = await makeWrapper({
@@ -178,10 +180,9 @@ describe('SubmitToCommunityLibrarySidePanel', () => {
178180
latestSubmission: null,
179181
});
180182

181-
const infoBoxes = wrapper.findAllComponents(Box).filter(box => box.props('kind') === 'info');
182-
expect(infoBoxes.length).toBe(1);
183-
const infoBox = infoBoxes.wrappers[0];
184-
expect(infoBox.text()).toContain(nonePrimaryInfo$());
183+
const infoSection = wrapper.find('.info-section');
184+
expect(infoSection.exists()).toBe(true);
185+
expect(infoSection.text()).toContain(nonePrimaryInfo$());
185186
});
186187

187188
it('when the previous submission was rejected', async () => {
@@ -191,10 +192,9 @@ describe('SubmitToCommunityLibrarySidePanel', () => {
191192
latestSubmission: { channel_version: 1, status: CommunityLibraryStatus.REJECTED },
192193
});
193194

194-
const infoBoxes = wrapper.findAllComponents(Box).filter(box => box.props('kind') === 'info');
195-
expect(infoBoxes.length).toBe(1);
196-
const infoBox = infoBoxes.wrappers[0];
197-
expect(infoBox.text()).toContain(flaggedPrimaryInfo$());
195+
const infoSection = wrapper.find('.info-section');
196+
expect(infoSection.exists()).toBe(true);
197+
expect(infoSection.text()).toContain(flaggedPrimaryInfo$());
198198
});
199199

200200
it('when the previous submission was approved', async () => {
@@ -204,11 +204,9 @@ describe('SubmitToCommunityLibrarySidePanel', () => {
204204
latestSubmission: { channel_version: 1, status: CommunityLibraryStatus.APPROVED },
205205
});
206206

207-
const infoBoxes = wrapper.findAllComponents(Box).filter(box => box.props('kind') === 'info');
208-
expect(infoBoxes.length).toBe(1);
209-
const infoBox = infoBoxes.wrappers[0];
210-
expect(infoBox.text()).toContain(approvedPrimaryInfo$());
211-
expect(infoBox.text()).toContain(reviewersWillSeeLatestFirst$());
207+
const infoSection = wrapper.find('.info-section');
208+
expect(infoSection.exists()).toBe(true);
209+
expect(infoSection.text()).toContain(approvedPrimaryInfo$());
212210
});
213211

214212
it('when the previous submission is pending', async () => {
@@ -218,11 +216,9 @@ describe('SubmitToCommunityLibrarySidePanel', () => {
218216
latestSubmission: { channel_version: 1, status: CommunityLibraryStatus.PENDING },
219217
});
220218

221-
const infoBoxes = wrapper.findAllComponents(Box).filter(box => box.props('kind') === 'info');
222-
expect(infoBoxes.length).toBe(1);
223-
const infoBox = infoBoxes.wrappers[0];
224-
expect(infoBox.text()).toContain(submittedPrimaryInfo$());
225-
expect(infoBox.text()).toContain(reviewersWillSeeLatestFirst$());
219+
const infoSection = wrapper.find('.info-section');
220+
expect(infoSection.exists()).toBe(true);
221+
expect(infoSection.text()).toContain(submittedPrimaryInfo$());
226222
});
227223
});
228224

@@ -256,17 +252,16 @@ describe('SubmitToCommunityLibrarySidePanel', () => {
256252
latestSubmission: null,
257253
});
258254

259-
let moreDetails = wrapper.find('[data-test="more-details"]');
260-
expect(moreDetails.exists()).toBe(false);
255+
const infoText = wrapper.find('.info-text');
256+
expect(infoText.text()).not.toContain('The Kolibri Community Library features channels');
261257

262-
let moreDetailsButton = wrapper.find('[data-test="more-details-button"]');
258+
const moreDetailsButton = wrapper.find('[data-test="more-details-button"]');
263259
await moreDetailsButton.trigger('click');
264260

265-
moreDetails = wrapper.find('.more-details-text');
266-
expect(moreDetails.exists()).toBe(true);
261+
expect(infoText.text()).toContain('The Kolibri Community Library features channels');
267262

268-
moreDetailsButton = wrapper.find('[data-test="more-details-button"]');
269-
expect(moreDetailsButton.exists()).toBe(false);
263+
const lessDetailsButton = wrapper.find('[data-test="less-details-button"]');
264+
expect(lessDetailsButton.exists()).toBe(true);
270265
});
271266
});
272267

@@ -478,6 +473,7 @@ describe('SubmitToCommunityLibrarySidePanel', () => {
478473
});
479474

480475
it('the panel closes', async () => {
476+
jest.useFakeTimers();
481477
const wrapper = await makeWrapper({
482478
channel: publishedNonPublicChannel,
483479
publishedData,
@@ -491,9 +487,11 @@ describe('SubmitToCommunityLibrarySidePanel', () => {
491487
await submitButton.trigger('click');
492488

493489
expect(wrapper.emitted('close')).toBeTruthy();
490+
jest.useRealTimers();
494491
});
495492

496493
it('a submission snackbar is shown', async () => {
494+
jest.useFakeTimers();
497495
const wrapper = await makeWrapper({
498496
channel: publishedNonPublicChannel,
499497
publishedData,
@@ -505,14 +503,15 @@ describe('SubmitToCommunityLibrarySidePanel', () => {
505503

506504
const submitButton = wrapper.find('[data-test="submit-button"]');
507505
await submitButton.trigger('click');
508-
509-
jest.useFakeTimers();
506+
await wrapper.vm.$nextTick();
510507

511508
expect(store.getters['snackbarIsVisible']).toBe(true);
512509
expect(CommunityLibrarySubmission.create).not.toHaveBeenCalled();
510+
jest.useRealTimers();
513511
});
514512

515513
it('the submission is created after a timeout', async () => {
514+
jest.useFakeTimers();
516515
const wrapper = await makeWrapper({
517516
channel: publishedNonPublicChannel,
518517
publishedData,
@@ -524,20 +523,22 @@ describe('SubmitToCommunityLibrarySidePanel', () => {
524523

525524
const countryField = wrapper.findComponent(CountryField);
526525
await countryField.vm.$emit('input', ['Czech Republic']);
527-
528-
jest.useFakeTimers();
526+
await wrapper.vm.$nextTick();
529527

530528
const submitButton = wrapper.find('[data-test="submit-button"]');
531529
await submitButton.trigger('click');
530+
await wrapper.vm.$nextTick();
532531

533532
jest.runAllTimers();
533+
await wrapper.vm.$nextTick();
534534

535535
expect(CommunityLibrarySubmission.create).toHaveBeenCalledWith({
536536
description: 'Some description',
537537
channel: publishedNonPublicChannel.id,
538538
countries: ['CZ'],
539539
categories: [Categories.SCHOOL],
540540
});
541+
jest.useRealTimers();
541542
});
542543
});
543544

0 commit comments

Comments
 (0)