Unverified Commit c1e4ae46 authored by Imran Salam's avatar Imran Salam Committed by GitHub
Browse files

Entity viewer sidebar toolstrip (#238)

* move track panel bar icons to sidebar folder

* replace TrackPanelBarIcon with ImageButton in TrackPanelBar

* add styling to sidebar icons and fix sidebar class names

* Add sidebar toolstrip to entity viewer

* change the following to lower case: toolStrip, sideBar, topBar

* use lowercase for topBar in all other places in the site

* fix storybook issues

* fix wrong sidebar class names

* get rid of sidebar configs

* Replace shortened EV with EntityViewer

* PR suggestions
parent 877a0243
Pipeline #58411 passed with stages
in 4 minutes and 29 seconds
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
margin-top: 50px; margin-top: 50px;
padding-left: 40px; padding-left: 40px;
&__emptyTopBar { &__emptyTopbar {
height: 40px; // same as top bar height in StandardAppLayout height: 40px; // same as top bar height in StandardAppLayout
background: $light-grey; background: $light-grey;
box-shadow: 0 2px 3px $grey; box-shadow: 0 2px 3px $grey;
......
...@@ -282,7 +282,7 @@ export const ExampleObjectLinks = (props: BrowserProps) => { ...@@ -282,7 +282,7 @@ export const ExampleObjectLinks = (props: BrowserProps) => {
return ( return (
<div> <div>
<div className={styles.exampleLinks__emptyTopBar} /> <div className={styles.exampleLinks__emptyTopbar} />
<div className={styles.exampleLinks}>{links}</div> <div className={styles.exampleLinks}>{links}</div>
</div> </div>
); );
......
...@@ -2,9 +2,7 @@ import React from 'react'; ...@@ -2,9 +2,7 @@ import React from 'react';
import { mount } from 'enzyme'; import { mount } from 'enzyme';
import { TrackPanelBar, TrackPanelBarProps } from './TrackPanelBar'; import { TrackPanelBar, TrackPanelBarProps } from './TrackPanelBar';
import TrackPanelBarIcon from './TrackPanelBarIcon'; import ImageButton from 'src/shared/components/image-button/ImageButton';
import { trackPanelBarConfig } from './trackPanelBarConfig';
describe('<TrackPanelBar />', () => { describe('<TrackPanelBar />', () => {
afterEach(() => { afterEach(() => {
...@@ -23,9 +21,7 @@ describe('<TrackPanelBar />', () => { ...@@ -23,9 +21,7 @@ describe('<TrackPanelBar />', () => {
describe('rendering', () => { describe('rendering', () => {
test('displays all track panel bar icons', () => { test('displays all track panel bar icons', () => {
const wrapper = mount(<TrackPanelBar {...defaultProps} />); const wrapper = mount(<TrackPanelBar {...defaultProps} />);
expect(wrapper.find(TrackPanelBarIcon).length).toBe( expect(wrapper.find(ImageButton).length).toBe(6);
trackPanelBarConfig.length
);
}); });
}); });
}); });
import React from 'react'; import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { trackPanelBarConfig, TrackPanelBarItem } from './trackPanelBarConfig';
import { import {
getIsTrackPanelModalOpened, getIsTrackPanelModalOpened,
getIsTrackPanelOpened, getIsTrackPanelOpened,
getTrackPanelModalView getTrackPanelModalView
} from '../trackPanelSelectors'; } from '../trackPanelSelectors';
import { RootState } from 'src/store';
import { import {
toggleTrackPanel, toggleTrackPanel,
closeTrackPanelModal, closeTrackPanelModal,
openTrackPanelModal openTrackPanelModal
} from '../trackPanelActions'; } from '../trackPanelActions';
import TrackPanelBarIcon from './TrackPanelBarIcon'; import ImageButton from 'src/shared/components/image-button/ImageButton';
import { ReactComponent as searchIcon } from 'static/img/sidebar/search.svg';
import { ReactComponent as tracksManagerIcon } from 'static/img/sidebar/tracks-manager.svg';
import { ReactComponent as bookmarkIcon } from 'static/img/sidebar/bookmark.svg';
import { ReactComponent as personalDataIcon } from 'static/img/sidebar/own-data.svg';
import { ReactComponent as shareIcon } from 'static/img/sidebar/share.svg';
import { ReactComponent as downloadIcon } from 'static/img/sidebar/download.svg';
import { RootState } from 'src/store';
import { Status } from 'src/shared/types/status';
import styles from 'src/shared/components/layout/StandardAppLayout.scss';
export type TrackPanelBarProps = { export type TrackPanelBarProps = {
isTrackPanelModalOpened: boolean; isTrackPanelModalOpened: boolean;
...@@ -26,20 +36,75 @@ export type TrackPanelBarProps = { ...@@ -26,20 +36,75 @@ export type TrackPanelBarProps = {
}; };
export const TrackPanelBar = (props: TrackPanelBarProps) => { export const TrackPanelBar = (props: TrackPanelBarProps) => {
const toggleModalView = (selectedItem: string) => {
if (!props.isTrackPanelOpened) {
props.toggleTrackPanel(true);
}
if (selectedItem === props.trackPanelModalView) {
props.closeTrackPanelModal();
} else {
props.openTrackPanelModal(selectedItem);
}
};
const getViewIconStatus = (selectedItem: string) => {
return selectedItem === props.trackPanelModalView &&
props.isTrackPanelOpened
? Status.SELECTED
: Status.UNSELECTED;
};
return ( return (
<> <>
{trackPanelBarConfig.map((item: TrackPanelBarItem) => ( <div className={styles.sidebarIcon} key="search">
<TrackPanelBarIcon <ImageButton
key={item.name} status={getViewIconStatus('search')}
iconConfig={item} description="Track search"
closeTrackPanelModal={props.closeTrackPanelModal} onClick={() => toggleModalView('search')}
openTrackPanelModal={props.openTrackPanelModal} image={searchIcon}
isTrackPanelModalOpened={props.isTrackPanelModalOpened} />
isTrackPanelOpened={props.isTrackPanelOpened} </div>
trackPanelModalView={props.trackPanelModalView} <div className={styles.sidebarIcon} key="tracks-manager">
toggleTrackPanel={props.toggleTrackPanel} <ImageButton
status={getViewIconStatus('tracks-manager')}
description="Tracks manager"
onClick={() => toggleModalView('tracks-manager')}
image={tracksManagerIcon}
/>
</div>
<div className={styles.sidebarIcon} key="bookmarks">
<ImageButton
status={getViewIconStatus('bookmarks')}
description="Bookmarks"
onClick={() => toggleModalView('bookmarks')}
image={bookmarkIcon}
/>
</div>
<div className={styles.sidebarIcon} key="personal-data">
<ImageButton
status={getViewIconStatus('personal-data')}
description="Personal data"
onClick={() => toggleModalView('personal-data')}
image={personalDataIcon}
/>
</div>
<div className={styles.sidebarIcon} key="share">
<ImageButton
status={getViewIconStatus('share')}
description="Share"
onClick={() => toggleModalView('share')}
image={shareIcon}
/>
</div>
<div className={styles.sidebarIcon} key="downloads">
<ImageButton
status={getViewIconStatus('downloads')}
description="Downloads"
onClick={() => toggleModalView('downloads')}
image={downloadIcon}
/> />
))} </div>
</> </>
); );
}; };
......
import React from 'react';
import { mount } from 'enzyme';
import TrackPanelBarIcon, { TrackPanelBarIconProps } from './TrackPanelBarIcon';
import ImageButton from 'src/shared/components/image-button/ImageButton';
import { createTrackPanelBarItem } from 'tests/fixtures/track-panel';
describe('<TrackPanelBarIcon />', () => {
afterEach(() => {
jest.resetAllMocks();
});
const defaultProps: TrackPanelBarIconProps = {
closeTrackPanelModal: jest.fn(),
iconConfig: createTrackPanelBarItem(),
isTrackPanelModalOpened: true,
isTrackPanelOpened: true,
openTrackPanelModal: jest.fn(),
trackPanelModalView: 'bookmarks',
toggleTrackPanel: jest.fn()
};
describe('rendering', () => {
test('renders image button', () => {
const wrapper = mount(<TrackPanelBarIcon {...defaultProps} />);
expect(wrapper.find(ImageButton)).toHaveLength(1);
});
});
});
import React, { useState, useEffect } from 'react';
import { TrackPanelBarItem } from './trackPanelBarConfig';
import ImageButton from 'src/shared/components/image-button/ImageButton';
import { Status } from 'src/shared/types/status';
import styles from './TrackPanelBarIcon.scss';
export type TrackPanelBarIconProps = {
closeTrackPanelModal: () => void;
iconConfig: TrackPanelBarItem;
isTrackPanelModalOpened: boolean;
isTrackPanelOpened: boolean;
openTrackPanelModal: (trackPanelModalView: string) => void;
trackPanelModalView: string;
toggleTrackPanel: (isTrackPanelOpened: boolean) => void;
};
const TrackPanelBarIcon = (props: TrackPanelBarIconProps) => {
const [isIconActive, setIconActive] = useState(false);
useEffect(() => {
if (!props.isTrackPanelModalOpened) {
setIconActive(false);
}
}, [props.isTrackPanelModalOpened]);
const toggleModalView = () => {
let newIconActiveState = !isIconActive;
if (!props.isTrackPanelOpened) {
props.toggleTrackPanel(true);
newIconActiveState = true;
} else if (props.iconConfig.name !== props.trackPanelModalView) {
newIconActiveState = true;
}
if (newIconActiveState) {
props.openTrackPanelModal(props.iconConfig.name);
} else {
props.closeTrackPanelModal();
}
setIconActive(newIconActiveState);
};
const getViewIconStatus = () => {
const { iconConfig, trackPanelModalView } = props;
return iconConfig.name === trackPanelModalView && props.isTrackPanelOpened
? Status.SELECTED
: Status.UNSELECTED;
};
return (
<div className={styles.barIcon}>
<ImageButton
status={getViewIconStatus()}
description={props.iconConfig.description}
onClick={toggleModalView}
image={props.iconConfig.icon}
/>
</div>
);
};
export default TrackPanelBarIcon;
import { ReactComponent as bookmarkIcon } from 'static/img/track-panel/bookmark.svg';
import { ReactComponent as downloadIcon } from 'static/img/track-panel/download.svg';
import { ReactComponent as searchIcon } from 'static/img/track-panel/search.svg';
import { ReactComponent as personalDataIcon } from 'static/img/track-panel/own-data.svg';
import { ReactComponent as shareIcon } from 'static/img/track-panel/share.svg';
import { ReactComponent as tracksManagerIcon } from 'static/img/track-panel/tracks-manager.svg';
export type TrackPanelBarItem = {
description: string;
icon: React.FunctionComponent<React.SVGProps<SVGSVGElement>>;
name: string;
};
export const trackPanelBarConfig: TrackPanelBarItem[] = [
{
description: 'Track search',
icon: searchIcon,
name: 'search'
},
{
description: 'Tracks manager',
icon: tracksManagerIcon,
name: 'tracks-manager'
},
{
description: 'Bookmarks',
icon: bookmarkIcon,
name: 'bookmarks'
},
{
description: 'Personal data',
icon: personalDataIcon,
name: 'personal-data'
},
{
description: 'Share',
icon: shareIcon,
name: 'share'
},
{
description: 'Downloads',
icon: downloadIcon,
name: 'downloads'
}
];
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
margin-top: 50px; margin-top: 50px;
padding-left: 100px; padding-left: 100px;
&__emptyTopBar { &__emptyTopbar {
height: 40px; // same as top bar height in StandardAppLayout height: 40px; // same as top bar height in StandardAppLayout
background: $light-grey; background: $light-grey;
box-shadow: 0 2px 3px $grey; box-shadow: 0 2px 3px $grey;
......
...@@ -15,8 +15,10 @@ import { setDataFromUrl } from 'src/content/app/entity-viewer/state/general/enti ...@@ -15,8 +15,10 @@ import { setDataFromUrl } from 'src/content/app/entity-viewer/state/general/enti
import { toggleSidebar } from 'src/content/app/entity-viewer/state/sidebar/entityViewerSidebarActions'; import { toggleSidebar } from 'src/content/app/entity-viewer/state/sidebar/entityViewerSidebarActions';
import { StandardAppLayout } from 'src/shared/components/layout'; import { StandardAppLayout } from 'src/shared/components/layout';
import EntityViewerSidebarTabs from 'src/content/app/entity-viewer/components/entity-viewer-sidebar-tabs/EntityViewerSidebarTabs';
import EntityViewerAppBar from 'src/content/app/entity-viewer/components/entity-viewer-app-bar/EntityViewerAppBar'; import EntityViewerAppBar from 'src/content/app/entity-viewer/components/entity-viewer-app-bar/EntityViewerAppBar';
import EntityViewerSidebar from './components/entity-viewer-sidebar/EntityViewerSideBar';
import EntityViewerSidebarTabs from './components/entity-viewer-sidebar-tabs/EntityViewerSidebarTabs';
import EntityViewerSidebarToolstrip from './components/entity-viewer-sidebar-toolstrip/EntityViewerSidebarToolstrip';
import { RootState } from 'src/store'; import { RootState } from 'src/store';
import { EnsObject } from 'src/shared/state/ens-object/ensObjectTypes'; import { EnsObject } from 'src/shared/state/ens-object/ensObjectTypes';
...@@ -52,8 +54,9 @@ const EntityViewer = (props: Props) => { ...@@ -52,8 +54,9 @@ const EntityViewer = (props: Props) => {
{params.entityId ? ( {params.entityId ? (
<StandardAppLayout <StandardAppLayout
mainContent={<div>Main content is coming...</div>} mainContent={<div>Main content is coming...</div>}
sidebarContent={<div>Sidebar content is coming...</div>} sidebarContent={<EntityViewerSidebar />}
sidebarNavigation={<EntityViewerSidebarTabs />} sidebarNavigation={<EntityViewerSidebarTabs />}
sidebarToolstripContent={<EntityViewerSidebarToolstrip />}
topbarContent={<div>Entity info summary goes here</div>} topbarContent={<div>Entity info summary goes here</div>}
isSidebarOpen={props.isSidebarOpen} isSidebarOpen={props.isSidebarOpen}
onSidebarToggle={props.toggleSidebar} onSidebarToggle={props.toggleSidebar}
...@@ -84,7 +87,7 @@ const ExampleLinks = (props: Props) => { ...@@ -84,7 +87,7 @@ const ExampleLinks = (props: Props) => {
return ( return (
<div> <div>
<div className={styles.exampleLinks__emptyTopBar} /> <div className={styles.exampleLinks__emptyTopbar} />
<div className={styles.exampleLinks}>{links}</div> <div className={styles.exampleLinks}>{links}</div>
</div> </div>
); );
......
import React from 'react';
import noop from 'lodash/noop';
import ImageButton from 'src/shared/components/image-button/ImageButton';
import { ReactComponent as searchIcon } from 'static/img/sidebar/search.svg';
import { ReactComponent as bookmarkIcon } from 'static/img/sidebar/bookmark.svg';
import { ReactComponent as shareIcon } from 'static/img/sidebar/share.svg';
import { ReactComponent as downloadIcon } from 'static/img/sidebar/download.svg';
import { Status } from 'src/shared/types/status';
import styles from 'src/shared/components/layout/StandardAppLayout.scss';
export const EntityViewerSidebarToolstrip = () => {
return (
<>
<div className={styles.sidebarIcon} key="search">
<ImageButton
status={Status.DISABLED}
description="Search"
onClick={noop}
image={searchIcon}
/>
</div>
<div className={styles.sidebarIcon} key="bookmarks">
<ImageButton
status={Status.DISABLED}
description="Bookmarks"
onClick={noop}
image={bookmarkIcon}
/>
</div>
<div className={styles.sidebarIcon} key="share">
<ImageButton
status={Status.DISABLED}
description="Share"
onClick={noop}
image={shareIcon}
/>
</div>
<div className={styles.sidebarIcon} key="downloads">
<ImageButton
status={Status.DISABLED}
description="Downloads"
onClick={noop}
image={downloadIcon}
/>
</div>
</>
);
};
export default EntityViewerSidebarToolstrip;
import React from 'react';
const EntityViewerSidebar = () => <div>Sidebar content is coming...</div>;
export default EntityViewerSidebar;
@import 'src/styles/common'; @import 'src/styles/common';
.topBar { .topbar {
display: flex; display: flex;
flex-wrap: nowrap; flex-wrap: nowrap;
justify-content: space-between; justify-content: space-between;
......
...@@ -35,7 +35,7 @@ export const Copyright = () => ( ...@@ -35,7 +35,7 @@ export const Copyright = () => (
export const Header = () => ( export const Header = () => (
<header> <header>
<div className={styles.topBar}> <div className={styles.topbar}>
<div> <div>
<HomeLink /> <HomeLink />
<ReleaseVersion /> <ReleaseVersion />
......
...@@ -11,7 +11,7 @@ import generalErrorImage3 from './images/general-error-3.jpg'; ...@@ -11,7 +11,7 @@ import generalErrorImage3 from './images/general-error-3.jpg';
const GeneralErrorScreen = () => ( const GeneralErrorScreen = () => (
<section className={styles.generalErrorScreen}> <section className={styles.generalErrorScreen}>
<header className={`${headerStyles.topBar} ${styles.generalErrorHeader}`}> <header className={`${headerStyles.topbar} ${styles.generalErrorHeader}`}>
<HomeLink /> <HomeLink />
<ReleaseVersion /> <ReleaseVersion />
<Copyright /> <Copyright />
......
@import 'src/styles/common'; @import 'src/styles/common';
$sideBarContentWidth: 320px; $sidebarContentWidth: 320px;
$sideBarToolStripWidth: 46px; $sidebarToolstripWidth: 46px;
$topBarHeight: 38px; $topbarHeight: 38px;
$drawerWindowWidth: 45px; $drawerWindowWidth: 45px;
.standardAppLayout { .standardAppLayout {
...@@ -11,8 +11,8 @@ $drawerWindowWidth: 45px; ...@@ -11,8 +11,8 @@ $drawerWindowWidth: 45px;
overflow: hidden; overflow: hidden;
} }
.topBar { .topbar {
height: $topBarHeight; height: $topbarHeight;
background: $light-grey; background: $light-grey;
box-shadow: 0 2px 3px $grey; box-shadow: 0 2px 3px $grey;
position: relative; position: relative;
...@@ -23,8 +23,8 @@ $drawerWindowWidth: 45px; ...@@ -23,8 +23,8 @@ $drawerWindowWidth: 45px;
&_withSidebarNavigation { &_withSidebarNavigation {
display: grid; display: grid;
align-items: center; align-items: center;
grid-template-columns: 1fr $sideBarContentWidth;