diff --git a/src/assets/images/flavour.png b/src/assets/images/flavour.png
new file mode 100644
index 00000000..3c4a2d4c
Binary files /dev/null and b/src/assets/images/flavour.png differ
diff --git a/src/components/CourseList.js b/src/components/CourseList.js
index 66312891..ef82c5f2 100644
--- a/src/components/CourseList.js
+++ b/src/components/CourseList.js
@@ -263,24 +263,37 @@ class CourseList extends Component {
{
- courses === null ? (
-
-
-
- ) : (
-
-
- {
- courses.results.map((course) => (
-
- ))
- }
-
-
- )
- }
+ courses === null ? (
+
+
+
+ ) : (null)
+ }
+ {
+ courses && courses.count === 0 ? (
+
+
+
+
+
+ ) : (
+
+
+ {
+ courses ? courses.results.map((course) => (
+
+ )) : (null)
+ }
+
+
+ )
+ }
{
courses && courses.total_pages > 1 ? (
@@ -333,6 +346,7 @@ CourseList.propTypes = {
fetchProgram: PropTypes.func.isRequired,
changeReadOnly: PropTypes.func.isRequired,
courses: PropTypes.shape({
+ count: PropTypes.number,
next: PropTypes.string,
previous: PropTypes.string,
total_pages: PropTypes.number,
diff --git a/src/components/ProgramCollection.js b/src/components/ProgramCollection.js
index 1da7a2e8..432a8988 100644
--- a/src/components/ProgramCollection.js
+++ b/src/components/ProgramCollection.js
@@ -1,4 +1,6 @@
import React, { Component } from 'react';
+import { Link } from 'react-router-dom';
+import { FormattedMessage, injectIntl } from 'react-intl';
import PropTypes from 'prop-types';
import { hot } from 'react-hot-loader';
import {
@@ -27,8 +29,8 @@ import {
Delete,
} from '@material-ui/icons';
import { Pagination, Autocomplete } from '@material-ui/lab';
-import { Link } from 'react-router-dom';
-import { FormattedMessage, injectIntl } from 'react-intl';
+
+import flavourImage from '@/assets/images/flavour.png';
const styles = (theme) => ({
mainContainer: {
@@ -139,7 +141,12 @@ class ProgramCollection extends Component {
tag,
classes,
} = this.props;
- const { ordering, tagFilters, sortMenuAnchorElement } = this.state;
+ const {
+ ordering,
+ searchQuery,
+ sortMenuAnchorElement,
+ tagFilters,
+ } = this.state;
const searchPlaceholder = intl.formatMessage({
id: 'app.program_collection.search',
@@ -289,7 +296,35 @@ class ProgramCollection extends Component {
{
- programs.results.map((program) => (
+ programs.count === 0 && !searchQuery && tagFilters.length === 0 ? (
+
+
+
+
+
+
+
+
+
+
+ ) : (null)
+ }
+ {
+ programs.count === 0 && (searchQuery || tagFilters.length !== 0) ? (
+
+
+
+
+
+ ) : programs.results.map((program) => (
{
const fetchProgram = jest.fn().mockResolvedValue();
const changeReadOnly = jest.fn();
const courses = {
+ count: 2,
next: null,
previous: null,
total_pages: 2,
@@ -100,6 +101,27 @@ describe('The CourseList component', () => {
expect(wrapper.find(CircularProgress).length).toBe(1);
});
+ test('shows message when no results', () => {
+ const empty = {
+ count: 0,
+ next: null,
+ previous: null,
+ total_pages: 1,
+ results: [],
+ };
+ const wrapper = shallowWithIntl(
+ ,
+ ).dive().dive().dive();
+
+ expect(wrapper.find(Card).exists()).toBe(false);
+ expect(wrapper.find('WithStyles(ForwardRef(Typography))').children().prop('defaultMessage')).toBe('Sorry, no courses match your filters.');
+ });
+
test('fetches courses after page change', () => {
const wrapper = shallowWithIntl(
{
test('renders on the page with no errors', () => {
const programs = {
+ count: 2,
next: null,
previous: null,
total_pages: 2,
@@ -59,6 +61,7 @@ describe('The ProgramCollection component', () => {
test('should set and clear sort menu anchor element when menu is opening and closing', () => {
const programs = {
+ count: 0,
next: null,
previous: null,
total_pages: 1,
@@ -82,8 +85,61 @@ describe('The ProgramCollection component', () => {
expect(wrapper.instance().state.sortMenuAnchorElement).toBe(null);
});
+ test('shows message when no results', () => {
+ const programs = {
+ count: 0,
+ next: null,
+ previous: null,
+ total_pages: 1,
+ results: [],
+ };
+ const wrapper = shallowWithIntl(
+ ,
+ ).dive().dive().dive();
+
+ wrapper.setState({
+ searchQuery: 'test',
+ });
+
+ expect(wrapper.find(Card).exists()).toBe(false);
+ expect(wrapper.find('img').exists()).toBe(false);
+ expect(wrapper.find('WithStyles(ForwardRef(Typography))').children().prop('defaultMessage')).toBe('Sorry, no programs match your filters.');
+ });
+
+ test('shows message when no programs', () => {
+ const programs = {
+ count: 0,
+ next: null,
+ previous: null,
+ total_pages: 1,
+ results: [],
+ };
+ const wrapper = shallowWithIntl(
+ ,
+ ).dive().dive().dive();
+
+ expect(wrapper.find(Card).exists()).toBe(false);
+ expect(wrapper.find('img').exists()).toBe(true);
+ expect(wrapper.find('WithStyles(ForwardRef(Typography))').children().prop('defaultMessage')).toBe('You don\'t have any programs yet!');
+ });
+
test('shows the correct number of programs for the user', () => {
const programs = {
+ count: 2,
next: null,
previous: null,
total_pages: 1,
@@ -122,6 +178,7 @@ describe('The ProgramCollection component', () => {
test('shows the correct number of programs for other users', () => {
const programs = {
+ count: 2,
next: null,
previous: null,
total_pages: 1,
@@ -160,6 +217,7 @@ describe('The ProgramCollection component', () => {
test('callback when program click', () => {
const programs = {
+ count: 2,
next: null,
previous: null,
total_pages: 1,
@@ -198,6 +256,7 @@ describe('The ProgramCollection component', () => {
test('callback when remove click', () => {
const programs = {
+ count: 2,
next: null,
previous: null,
total_pages: 1,
@@ -235,6 +294,7 @@ describe('The ProgramCollection component', () => {
test('callback when page changes', () => {
const programs = {
+ count: 2,
next: null,
previous: null,
total_pages: 2,
@@ -276,6 +336,7 @@ describe('The ProgramCollection component', () => {
test('callback when search changes', () => {
const programs = {
+ count: 2,
next: null,
previous: null,
total_pages: 2,
@@ -322,6 +383,7 @@ describe('The ProgramCollection component', () => {
test('callback when order changes', () => {
const programs = {
+ count: 2,
next: null,
previous: null,
total_pages: 2,
@@ -379,6 +441,7 @@ describe('The ProgramCollection component', () => {
test('callback when tag changes', () => {
const programs = {
+ count: 2,
next: null,
previous: null,
total_pages: 2,
diff --git a/src/components/__tests__/__snapshots__/ProgramCollection.test.js.snap b/src/components/__tests__/__snapshots__/ProgramCollection.test.js.snap
index c7c8d423..7c4b1884 100644
--- a/src/components/__tests__/__snapshots__/ProgramCollection.test.js.snap
+++ b/src/components/__tests__/__snapshots__/ProgramCollection.test.js.snap
@@ -31,6 +31,7 @@ exports[`The ProgramCollection component renders on the page with no errors 1`]
onUpdate={[MockFunction]}
programs={
Object {
+ "count": 2,
"next": null,
"previous": null,
"results": Array [
@@ -100,6 +101,7 @@ exports[`The ProgramCollection component renders on the page with no errors 1`]
onUpdate={[MockFunction]}
programs={
Object {
+ "count": 2,
"next": null,
"previous": null,
"results": Array [
@@ -175,6 +177,7 @@ exports[`The ProgramCollection component renders on the page with no errors 1`]
onUpdate={[MockFunction]}
programs={
Object {
+ "count": 2,
"next": null,
"previous": null,
"results": Array [
diff --git a/src/translations/locales/en.json b/src/translations/locales/en.json
index 3c14c032..6558b677 100644
--- a/src/translations/locales/en.json
+++ b/src/translations/locales/en.json
@@ -23,6 +23,7 @@
"app.control.run": "Run",
"app.control.step": "Step",
"app.control.stop": "Stop",
+ "app.course_list.nothing": "Sorry, no courses match your filters.",
"app.course_list.search": "Search courses",
"app.course_list.title": "Courses",
"app.global.not_exist": "Sorry, the page you're looking for doesn't exist.",
@@ -47,6 +48,7 @@
"app.mission_control.console": "Debug Console",
"app.mission_control.details": "Project Details",
"app.mission_control.edit_details": "Edit Project Details",
+ "app.mission_control.goal_label": "Goal",
"app.mission_control.left": "Left",
"app.mission_control.light_sensors": "Light Sensors",
"app.mission_control.milliVolts": "milliVolts",
@@ -71,6 +73,7 @@
"app.program_collection.filter": "Filter by tag",
"app.program_collection.name": "Name",
"app.program_collection.new": "New Program",
+ "app.program_collection.nothing": "Sorry, no programs match your filters.",
"app.program_collection.remove": "Delete",
"app.program_collection.search": "Search programs",
"app.program_collection.sort": "Sort",
diff --git a/src/translations/locales/es.json b/src/translations/locales/es.json
index 7a1839ae..9462a1ba 100644
--- a/src/translations/locales/es.json
+++ b/src/translations/locales/es.json
@@ -23,6 +23,7 @@
"app.control.run": "Ejecutar",
"app.control.step": "Avanzar",
"app.control.stop": "Parar",
+ "app.course_list.nothing": "Sorry, no courses match your filters.",
"app.course_list.search": "Search courses",
"app.course_list.title": "Courses",
"app.global.not_exist": "Perdón, la página que estás buscando no existe..",
@@ -47,6 +48,7 @@
"app.mission_control.console": "Depurar Consola",
"app.mission_control.details": "Project Details",
"app.mission_control.edit_details": "Edit Project Details",
+ "app.mission_control.goal_label": "Goal",
"app.mission_control.left": "Left",
"app.mission_control.light_sensors": "Light Sensors",
"app.mission_control.milliVolts": "milliVolts",
@@ -71,6 +73,7 @@
"app.program_collection.filter": "Filter by tag",
"app.program_collection.name": "Nombre",
"app.program_collection.new": "New Program",
+ "app.program_collection.nothing": "Sorry, no programs match your filters.",
"app.program_collection.remove": "Quitar",
"app.program_collection.search": "Buscar",
"app.program_collection.sort": "Ordenar",