diff --git a/ui/src/album/albumLists.js b/ui/src/album/albumLists.js index e38bcb612..c18c38ffc 100644 --- a/ui/src/album/albumLists.js +++ b/ui/src/album/albumLists.js @@ -1,40 +1,80 @@ +import React from 'react' import ShuffleIcon from '@material-ui/icons/Shuffle' import LibraryAddIcon from '@material-ui/icons/LibraryAdd' import VideoLibraryIcon from '@material-ui/icons/VideoLibrary' import RepeatIcon from '@material-ui/icons/Repeat' import AlbumIcon from '@material-ui/icons/Album' import FavoriteIcon from '@material-ui/icons/Favorite' +import FavoriteBorderIcon from '@material-ui/icons/FavoriteBorder' import StarIcon from '@material-ui/icons/Star' +import StarBorderIcon from '@material-ui/icons/StarBorder' +import AlbumOutlinedIcon from '@material-ui/icons/AlbumOutlined' +import LibraryAddOutlinedIcon from '@material-ui/icons/LibraryAddOutlined' +import VideoLibraryOutlinedIcon from '@material-ui/icons/VideoLibraryOutlined' import config from '../config' +import DynamicMenuIcon from '../layout/DynamicMenuIcon' export default { all: { - icon: AlbumIcon, + icon: ( + + ), params: 'sort=name&order=ASC', }, - random: { icon: ShuffleIcon, params: 'sort=random&order=ASC' }, + random: { + icon: , + params: 'sort=random&order=ASC', + }, ...(config.enableFavourites && { starred: { - icon: FavoriteIcon, + icon: ( + + ), params: 'sort=starred_at&order=DESC&filter={"starred":true}', }, }), ...(config.enableStarRating && { topRated: { - icon: StarIcon, + icon: ( + + ), params: 'sort=rating&order=DESC&filter={"has_rating":true}', }, }), recentlyAdded: { - icon: LibraryAddIcon, + icon: ( + + ), params: 'sort=recently_added&order=DESC', }, recentlyPlayed: { - icon: VideoLibraryIcon, + icon: ( + + ), params: 'sort=play_date&order=DESC&filter={"recently_played":true}', }, mostPlayed: { - icon: RepeatIcon, + icon: , params: 'sort=play_count&order=DESC&filter={"recently_played":true}', }, } diff --git a/ui/src/album/index.js b/ui/src/album/index.js index dc1a684a4..1cdb9d05a 100644 --- a/ui/src/album/index.js +++ b/ui/src/album/index.js @@ -1,9 +1,7 @@ -import AlbumIcon from '@material-ui/icons/Album' import AlbumList from './AlbumList' import AlbumShow from './AlbumShow' export default { list: AlbumList, show: AlbumShow, - icon: AlbumIcon, } diff --git a/ui/src/artist/index.js b/ui/src/artist/index.js index 1cd69b12c..206bfb6cd 100644 --- a/ui/src/artist/index.js +++ b/ui/src/artist/index.js @@ -1,7 +1,16 @@ -import MicIcon from '@material-ui/icons/Mic' +import React from 'react' import ArtistList from './ArtistList' +import DynamicMenuIcon from '../layout/DynamicMenuIcon' +import MicNoneOutlinedIcon from '@material-ui/icons/MicNoneOutlined' +import MicIcon from '@material-ui/icons/Mic' export default { list: ArtistList, - icon: MicIcon, + icon: ( + + ), } diff --git a/ui/src/layout/DynamicMenuIcon.js b/ui/src/layout/DynamicMenuIcon.js new file mode 100644 index 000000000..e86bff6ca --- /dev/null +++ b/ui/src/layout/DynamicMenuIcon.js @@ -0,0 +1,23 @@ +import PropTypes from 'prop-types' +import { useLocation } from 'react-router-dom' +import { createElement } from 'react' + +const DynamicMenuIcon = ({ icon, activeIcon, path }) => { + const location = useLocation() + + if (!activeIcon) { + return createElement(icon, { 'data-testid': 'icon' }) + } + + return location.pathname.startsWith('/' + path) + ? createElement(activeIcon, { 'data-testid': 'activeIcon' }) + : createElement(icon, { 'data-testid': 'icon' }) +} + +DynamicMenuIcon.propTypes = { + path: PropTypes.string.isRequired, + icon: PropTypes.object.isRequired, + activeIcon: PropTypes.object, +} + +export default DynamicMenuIcon diff --git a/ui/src/layout/DynamicMenuIcon.test.js b/ui/src/layout/DynamicMenuIcon.test.js new file mode 100644 index 000000000..a48ed9aff --- /dev/null +++ b/ui/src/layout/DynamicMenuIcon.test.js @@ -0,0 +1,56 @@ +import * as React from 'react' +import { cleanup, render } from '@testing-library/react' +import { createMemoryHistory } from 'history' +import { Router } from 'react-router-dom' +import StarIcon from '@material-ui/icons/Star' +import StarBorderIcon from '@material-ui/icons/StarBorder' +import DynamicMenuIcon from './DynamicMenuIcon' + +describe('', () => { + it('renders icon if no activeIcon is specified', () => { + const history = createMemoryHistory() + const route = '/test' + history.push(route) + + const { getByTestId } = render( + + + + ) + expect(getByTestId('icon')).not.toBeNull() + }) + + it('renders icon if path does not match the URL', () => { + const history = createMemoryHistory() + const route = '/path' + history.push(route) + + const { getByTestId } = render( + + + + ) + expect(getByTestId('icon')).not.toBeNull() + }) + + it('renders activeIcon if path matches the URL', () => { + const history = createMemoryHistory() + const route = '/path' + history.push(route) + + const { getByTestId } = render( + + + + ) + expect(getByTestId('activeIcon')).not.toBeNull() + }) +}) diff --git a/ui/src/layout/Menu.js b/ui/src/layout/Menu.js index 9aae9c05d..820d6a5ae 100644 --- a/ui/src/layout/Menu.js +++ b/ui/src/layout/Menu.js @@ -1,6 +1,6 @@ -import React, { useState, createElement } from 'react' +import React, { useState } from 'react' import { useSelector } from 'react-redux' -import { useMediaQuery } from '@material-ui/core' +import { makeStyles, useMediaQuery } from '@material-ui/core' import { useTranslate, MenuItemLink, getResources } from 'react-admin' import { withRouter } from 'react-router-dom' import LibraryMusicIcon from '@material-ui/icons/LibraryMusic' @@ -11,6 +11,13 @@ import inflection from 'inflection' import albumLists from '../album/albumLists' import { HelpDialog } from '../dialogs' +const useStyles = makeStyles((theme) => ({ + active: { + color: theme.palette.text.primary, + fontWeight: 'bold', + }, +})) + const translatedResourceName = (resource, translate) => translate(`resources.${resource.name}.name`, { smart_count: 2, @@ -27,6 +34,7 @@ const Menu = ({ onMenuClick, dense, logout }) => { const isXsmall = useMediaQuery((theme) => theme.breakpoints.down('xs')) const open = useSelector((state) => state.admin.ui.sidebarOpen) const translate = useTranslate() + const classes = useStyles() const resources = useSelector(getResources) // TODO State is not persisted in mobile when you close the sidebar menu. Move to redux? @@ -44,10 +52,9 @@ const Menu = ({ onMenuClick, dense, logout }) => { - } + leftIcon={resource.icon || } onClick={onMenuClick} sidebarIsOpen={open} dense={dense} @@ -70,8 +77,9 @@ const Menu = ({ onMenuClick, dense, logout }) => { } + leftIcon={al.icon || } onClick={onMenuClick} sidebarIsOpen={open} dense={dense} diff --git a/ui/src/playlist/index.js b/ui/src/playlist/index.js index 19530f8e8..3a7111ddb 100644 --- a/ui/src/playlist/index.js +++ b/ui/src/playlist/index.js @@ -1,13 +1,22 @@ -import PlaylistIcon from '../icons/Playlist' +import React from 'react' +import QueueMusicOutlinedIcon from '@material-ui/icons/QueueMusicOutlined' +import QueueMusicIcon from '@material-ui/icons/QueueMusic' +import DynamicMenuIcon from '../layout/DynamicMenuIcon' import PlaylistList from './PlaylistList' import PlaylistEdit from './PlaylistEdit' import PlaylistCreate from './PlaylistCreate' import PlaylistShow from './PlaylistShow' export default { - icon: PlaylistIcon, list: PlaylistList, create: PlaylistCreate, edit: PlaylistEdit, show: PlaylistShow, + icon: ( + + ), } diff --git a/ui/src/song/index.js b/ui/src/song/index.js index 949bd6184..bbd3b2086 100644 --- a/ui/src/song/index.js +++ b/ui/src/song/index.js @@ -1,7 +1,16 @@ -import MusicNoteIcon from '@material-ui/icons/MusicNote' +import React from 'react' import SongList from './SongList' +import MusicNoteOutlinedIcon from '@material-ui/icons/MusicNoteOutlined' +import MusicNoteIcon from '@material-ui/icons/MusicNote' +import DynamicMenuIcon from '../layout/DynamicMenuIcon' export default { list: SongList, - icon: MusicNoteIcon, + icon: ( + + ), }