I'm trying to implement a generic sidebar in my react project.
* I used prime react sidebar.
* It can be used multiple times in the page/application
* It loads different prime react buttons for every sidebar.
* For every sidebar its own onclick function is called when buttons has clicked.
Example screenshots:
project structure:
index.tsx
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { createStore, combineReducers } from 'redux';
import { Provider } from 'react-redux';
import App from './components/app';
import reducer from './reducer/reducer';
const rootReducer = combineReducers(
{ reducer: reducer}
);
const store = createStore(rootReducer);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
app.tsx
import React from 'react';
import { connect } from 'react-redux';
import { BrowserRouter as Router } from 'react-router-dom';
import Home from './home/home';
import { SET_USER } from '../static/reducerActionTypes';
interface IDispatchProps {
setUser: (user: string) => void;
}
type IProp = IDispatchProps;
class App extends React.PureComponent<IProp> {
constructor(props: IProp) {
super(props);
}
componentDidMount() {
const { setUser } = this.props;
setUser('Eda Merdan Biricik');
}
componentWillUnmount() {
const { setUser } = this.props;
setUser('');
}
render() {
return (
<Router>
<div className="app">
<Home />
</div>
</Router>
);
}
}
const mapDispatchToProps = (dispatch: any) => ({
setUser: (user: string) => {
dispatch({
user,
type: SET_USER,
});
},
});
export default connect(null, mapDispatchToProps)(App);
home.tsx
import React from 'react';
import MyLeftSidebar from "../sidebar/myLeftSidebar";
import MyRightSidebar from "../sidebar/myRightSidebar";
export default () => (
<>
<div>
<MyLeftSidebar />
</div>
<div>
<MyRightSidebar />
</div>
</>
);
myLeftSidebar.tsx
import React from 'react';
import { connect } from 'react-redux';
import CommonSidebar from '../../commons/sidebar/commonSidebar';
const sidebarParameters = {
fetchApiParameter: 'left',
reducerKey: 'leftSidebarButtons',
position: 'left',
};
interface IStateProps {
user: string;
}
type IProp = IStateProps;
class MyLeftSidebar extends React.PureComponent<IProp> {
constructor(props: IProp) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick(params: any) {
alert('do want you want. my params: ' + params);
}
render() {
const { user } = this.props;
return (
<>
{user !== null &&
<CommonSidebar
handleOnClick={this.handleClick}
sidebarParameters={sidebarParameters}
/>
}
</>
);
}
}
const mapStateToProps = ({ reducer }: any): IStateProps => {
return {
user: reducer.get('user', null),
};
};
export default connect(mapStateToProps, null)(MyLeftSidebar);
myRightSidebar.tsx
import React from 'react';
import { connect } from 'react-redux';
import CommonSidebar from '../../commons/sidebar/commonSidebar';
const sidebarParameters = {
fetchApiParameter: 'right',
reducerKey: 'rightSidebarButtons',
position: 'right',
};
interface IStateProps {
user: string;
}
type IProp = IStateProps;
class MyRightSidebar extends React.PureComponent<IProp> {
constructor(props: IProp) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick(params: any) {
alert('do want you want. my params: ' + params);
}
render() {
const { user } = this.props;
return (
<>
{user !== null &&
<CommonSidebar
handleOnClick={this.handleClick}
sidebarParameters={sidebarParameters}
/>
}
</>
);
}
}
const mapStateToProps = ({ reducer }: any): IStateProps => {
return {
user: reducer.get('user', null),
};
};
export default connect(mapStateToProps, null)(MyRightSidebar);
reducerActionTypes.ts
export const SET_SIDEBAR_BUTTONS = 'SET_SIDEBAR_BUTTONS';
export const SET_USER = 'SET_USER';
reducer.ts
import I from 'immutable';
import { SET_SIDEBAR_BUTTONS, SET_USER } from '../static/reducerActionTypes';
interface Action {
type: string;
data: any;
user: string;
}
export default (state: any = I.Map(), action: Action) => {
switch (action.type) {
case SET_USER:
return state.set('user', action.user);
case SET_SIDEBAR_BUTTONS:
const { data: { key = '', buttons = I.Map() } = {} } = action;
return state.set(key, buttons);
default:
return state;
}
};
commonSidebar.tsx
import React from 'react';
import I from 'immutable';
import { connect } from 'react-redux';
import { Sidebar } from 'primereact/sidebar';
import { CommonSidebarButton } from './commonSidebarButton';
import { getSidebarButtons } from '../../apis/sidebarAPI';
import { SET_SIDEBAR_BUTTONS } from '../../static/reducerActionTypes';
import 'primereact/resources/themes/nova-light/theme.css';
import 'primereact/resources/primereact.min.css';
import './commonSidebar.scss';
interface ISidebarParameters {
fetchApiParameter: string;
reducerKey: string;
position: string;
}
interface IOwnProps {
handleOnClick: (params: object) => void;
sidebarParameters: ISidebarParameters;
}
interface IDispatchProps {
setSidebarButtons: (buttons: any) => void;
}
interface IStateProps {
sidebarButtons: any;
user: string;
}
type IProp = IOwnProps & IDispatchProps & IStateProps;
class CommonSidebar extends React.PureComponent<IProp> {
constructor(props: IProp) {
super(props);
this.renderButtons = this.renderButtons.bind(this);
}
async componentDidMount() {
const { setSidebarButtons, sidebarParameters, user } = this.props;
const buttons = await getSidebarButtons(sidebarParameters.fetchApiParameter, user);
setSidebarButtons(buttons);
}
componentWillUnmount(): void {
const { setSidebarButtons } = this.props;
setSidebarButtons(I.Map());
}
renderButtons() {
const { sidebarButtons } = this.props;
const isSuccess = I.List.isList(sidebarButtons);
const sortedSidebarButtons = isSuccess && sidebarButtons.sortBy((s: any) => s.get('orderNo'));
return(
<div >
{isSuccess &&
sortedSidebarButtons.map((button:any, idx: number) => (
<CommonSidebarButton
button={button}
handleOnClick={this.props.handleOnClick}
key={idx}
/>
))
}
</div>
);
}
render() {
const { position } = this.props.sidebarParameters;
return(
<Sidebar
className="common-sidebar"
modal={false}
visible
onHide={() => null}
showCloseIcon={false}
baseZIndex={1}
position={position}
>
{this.renderButtons()}
</Sidebar>
);
}
}
const mapStateToProps = ({ reducer }: any, ownProps: IOwnProps): IStateProps => {
return {
sidebarButtons: reducer.getIn([ownProps.sidebarParameters.reducerKey, 'data'], I.List()),
user: reducer.get('user', ''),
};
};
const mapDispatchToProps = (dispatch: any, ownProps: IOwnProps): IDispatchProps => ({
setSidebarButtons: (data: any) => {
dispatch({
data: { buttons: data, key: ownProps.sidebarParameters.reducerKey },
type: SET_SIDEBAR_BUTTONS,
});
},
});
export default connect<IStateProps, IDispatchProps, IOwnProps>(
mapStateToProps,
mapDispatchToProps
)(CommonSidebar);
commonSidebarButton.tsx
import React from 'react';
import { Button } from 'primereact/button';
import 'primereact/resources/themes/nova-light/theme.css';
import 'primereact/resources/primereact.min.css';
import 'primeicons/primeicons.css';
import './commonSidebar.scss';
export const CommonSidebarButton = ({ button, handleOnClick }: any) => (
<Button
label={button.get('iconLabel')}
icon={button.get('iconName')}
onClick={() => handleOnClick(button.get('payload'))}
/>
);
commonSidebar.scss
body {
div {
.common-sidebar {
background: #2c7f33;
width: 70px;
}
}
}
body {
.p-sidebar {
border: 0px ;
padding:0px;
}
.p-button {
background-color: transparent;
border-color: #2c7f33;
color: #ffffff;
font-size: 12px;
padding: 10px 5px;
margin-top: 10px;
}
.p-button.p-button-icon-only {
width: 100%;
}
.pi {
font-size: 3.00em;
}
.p-button.p-button-text-icon-left .p-button-text {
padding: 0px;
}
.p-button.p-button-icon-only .p-button-text {
display: none;
}
.p-button:enabled:active {
background-color: #1b9e1c;
border-color: #ffffff;
}
.p-button:enabled:focus {
box-shadow: 0 0 0 0.1em red;
}
.p-button:enabled:hover {
background-color: #1b9e1c;
border-color: #ffffff;
}
}
.p-button-icon-only .p-button-icon-left, .p-button-text-icon-left .p-button-icon-left {
position: initial;
}
.p-button-text {
color: #ffffff;
font-size: 9px;
}
.p-button-icon-only .p-button-icon-left{
margin-top: 0px;
margin-left: 0px;
}
sidebarAPI.ts
import axios from 'axios';
import I from 'immutable';
export const getSidebarButtons = async(sidebarType: string, user: string) => {
try {
const { data } = await axios.get(
`http://localhost.../getSidebarButtons?sidebarType=${sidebarType}&user=${user}`);
return I.fromJS({ data });
} catch (e) {
return I.fromJS({
data: {
error: 'Error while fetching sidebar buttons',
},
});
}
};
For this dynamic loading a rest api has called. It takes 2 parameter -sidebarType, user- to decide which parameters should return.
An example of rest api result: (sidebarType is 'left')
[{
"id":"1","orderNo":"2","iconName":"pi pi-bell",
"iconLabel":"Give Your Icon Label If You Want",
"payload":{"params":{"componentPath":"/test"}}
},
{
"id":"2","orderNo":"3","iconName":"pi pi-check",
"payload":{"params":{"componentPath":"/test"}}
},
{
"id":"3","orderNo":"1","iconName":"pi pi-times",
"iconLabel":"Give Your Icon Label If You Want",
"payload":{"params":{"componentPath":"/test"}}
}]
for sidebarType = 'right'
[{
"id":"1","orderNo":"2","iconName":"pi pi-bell",
"iconLabel":"Give Your Icon Label If You Want",
"payload":{"params":{"componentPath":"/test"}}
}]