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 ? ( + + + Kids + + + + + + + + ) : (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",