i am working on react and i want to add diffrent routes like admin and driver. I have two route object for each claims.I am getting user's role from api and i want to showing route by role(claim)
what i have tried,
i added that condition line -> Role==="Admin"? but i got error that Cannot find menuItem
what should i do? thanks in advance
const getRole="Admin" //for example
const admin= {
id: 'dashboard',
title: '',
type: 'group',
children: [
{
id: 'default',
title: 'addShipment',
type: 'item',
url: '/dashboard/addShipment',
icon: icons.IconDashboard,
breadcrumbs: false
},
{
id: 'default',
title: 'showShipment',
type: 'item',
url: '/dashboard/showShipment',
icon: icons.IconDashboard,
breadcrumbs: false
}
]
};
const driver= {
id: 'dashboard',
title: '',
type: 'group',
children: [
{
id: 'default',
title: 'showShipment',
type: 'item',
url: '/dashboard/showShipment',
icon: icons.IconDashboard,
breadcrumbs: false
}
]
};
{Role==="Admin"?
(
const menuItem = {
items: [admin]
};
)
:(
const menuItem = {
items: [driver]
};
)
const MenuList = () => {
const navItems = menuItem?.items.map((item) => {
switch (item.type) {
case 'group':
return <NavGroup key={item.id} item={item} />;
default:
return (
<Typography key={item.id} variant="h6" color="error" align="center">
Menu Items Error
</Typography>
);
}
});
return <>{navItems}</>;};
export default MenuList;
This is not the right way to initialize a variable:
{Role==="Admin"?
(
const menuItem = {
items: [admin]
};
)
:(
const menuItem = {
items: [driver]
};
)
}
The variable menuItem is only present within the scope of that condition.
If you want to use a ternary operator try this:
const menuItem = Role === "Admin" ? { items: [admin] } : { items: [driver] }
OR
const menuItem = { items: [Role === "Admin" ? admin: driver] }
Related
Im making a react page with fluent ui and i wanted to open a certain component (documentpane.tsx) when i click a certain button from my command bar.
this is my commandbar code:
const theme = getTheme();
// Styles for both command bar and overflow/menu items
const itemStyles: Partial<IContextualMenuItemStyles> = {
label: { fontSize: 18 },
icon: { color: uPrinceTheme.palette.themePrimary },
iconHovered: { color: uPrinceTheme.palette.themeSecondary },
};
// For passing the styles through to the context menus
const menuStyles: Partial<IContextualMenuStyles> = {
subComponentStyles: { menuItem: itemStyles, callout: {} },
};
const getCommandBarButtonStyles = memoizeFunction(
(originalStyles: IButtonStyles | undefined): Partial<IContextualMenuItemStyles> => {
if (!originalStyles) {
return itemStyles;
}
return concatStyleSets(originalStyles, itemStyles);
},
);
// Custom renderer for main command bar items
const CustomButton: React.FunctionComponent<IButtonProps> = props => {
const buttonOnMouseClick = () => alert(`${props.text} clicked`);
// eslint-disable-next-line react/jsx-no-bind
return <CommandBarButton {...props} onClick={buttonOnMouseClick} styles={getCommandBarButtonStyles(props.styles)} />;
};
// Custom renderer for menu items (these must have a separate custom renderer because it's unlikely
// that the same component could be rendered properly as both a command bar item and menu item).
// It's also okay to custom render only the command bar items without changing the menu items.
const CustomMenuItem: React.FunctionComponent<IContextualMenuItemProps> = props => {
// Due to ContextualMenu implementation quirks, passing styles or onClick here doesn't work.
// The onClick handler must be on the ICommandBarItemProps item instead (_overflowItems in this example).
return <ContextualMenuItem {...props} />;
};
const overflowProps: IButtonProps = {
ariaLabel: 'More commands',
menuProps: {
contextualMenuItemAs: CustomMenuItem,
// Styles are passed through to menu items here
styles: menuStyles,
items: [], // CommandBar will determine items rendered in overflow
isBeakVisible: true,
beakWidth: 20,
gapSpace: 10,
directionalHint: DirectionalHint.topCenter,
},
};
export const CommandBarButtonAsExample: React.FunctionComponent = () => {
return (
<CommandBar
overflowButtonProps={overflowProps}
// Custom render all buttons
buttonAs={CustomButton}
items={_items}
ariaLabel="Use left and right arrow keys to navigate between commands"
/>
);
};
const _items: ICommandBarItemProps[] = [
{
key: 'newItem',
text: 'Create',
iconProps: { iconName: 'Add' },
},
{
key: 'upload',
text: 'Read',
iconProps: { iconName: 'Read' },
href: 'https://developer.microsoft.com/en-us/fluentui',
},
{ key: 'share', text: 'Update', iconProps: { iconName: 'Share' } },
{ key: 'download', text: 'Delete', iconProps: { iconName: 'Delete' } },
];
export default CommandBarButtonAsExample;
and this is my index.tsx now:
import { StrictMode } from "react";
import ReactDOM from "react-dom";
import { initializeIcons } from "#fluentui/react/lib/Icons";
import App from "./App";
const rootElement = document.getElementById("root");
initializeIcons();
ReactDOM.render(
<StrictMode>
<App />
</StrictMode>,
rootElement
);
i will also add a git repo with my code in it so you guys can see it better.
https://github.com/robbe-delsoir/app2
thank you very much for the help!
Robbe
Add an onClick method in the commandbar item which sets the show component state for documentpane.tsx.
const _items: ICommandBarItemProps[] = [
{
key: 'newItem',
text: 'Create',
iconProps: { iconName: 'Add' },
},
{
key: 'upload',
text: 'Read',
iconProps: { iconName: 'Read' },
onClick:{ () => { setShowDocumentPane(true) } }
href: 'https://developer.microsoft.com/en-us/fluentui',
},
{ key: 'share', text: 'Update', iconProps: { iconName: 'Share' } },
{ key: 'download', text: 'Delete', iconProps: { iconName: 'Delete' } },
];
I have some nested data that needs to generate a form of checkboxes dynamically. The "Tasks" data, needs a parent checkbox, as per MaterialUI's docs under "Indeterminate" in their Checkboxes example . I'm struggling to understand how to apply their example in conjunction with my code.
Current data used to generate dynamic checkboxes:
const availableFilters = useMemo(
() => [
{
title: "Status",
filterOptions: [
{ label: "Ready for Review"},
{ label: "Ready for Techcheck"},
],
},
{
title: "Offices",
filterOptions:
[
{ label: "London" },
{ label: "Berlin"},
}],
},
{
title: "Tasks",
filterGroups:
[
{
title: "3D"
filterOptions: [
{label: "Animation"},
{label: "Lighting"},
],
},
{
title: "Comp"
filterOptions: [
{label: "Compositing"},
{label: "Prep"}
],
},
],
},
{
title: "Creator",
filterOptions: [{ label: "Alex"}, { label: "John"}],
},
],
[filterInfo, taskGroups]
);
Getting confused rather easily with the nesting and some recursive typescript stuff.
This is the handlerFunction in the parent component(AddtoReviewMenu) with "Lodash":
const [checkedValues, setCheckedValues] = useState<{[key: string]: string[];}>({});
const handleCheckboxChange = useCallback(
(checked: boolean, title: string, value: string) => {
if(checked) {
if (Object.keys(checkedValues).includes(title)) {
setCheckedValues({
...checkedValues,
[title]: [...checkedValues[title], value],
});
} else {
setCheckedValues({
...checkedValues,
[title]: [value],
});
} else {
setCheckedValues({
...checkedValues,
[title]: _(checkedValues[title])
.filter((c) => c !== value)
.value(),
});
}
},
[checkedValues]
);
Here is the child component that populates the the checkboxes based on the data:
import React from "react";
import IconButton, { IconButtonProps } from "#mui/material/IconButton";
import Box, { FormControl, Stack } from "#mui/material/";
interface ExpandMoreProps extends IconButtonProps {
expand: Boolean;
}
const ExpandMore = styled((props: ExpandMoreProps) => {
const { expand, ...other } = props;
return <IconButton {...other} />;
});
interface Props extends FilterGroup {
handleCheckboxChange: (
checked: boolean,
title: string,
value: string
) => void;
}
export default function FilterOptionGroup(props: Props) {
const { filterGroups, title, handleCheckboxChange } = props;
const [expanded, setExpanded] = useState(true);
const handleExpandClick = () => {
setExpanded(!expanded);
};
return (
<Box>
<FormControl>
<Stack>
<ExpandMore expand={expanded} onClick={handleExpandClick}>
<ArrowUpIcon />
</ExpandMore>
<FormLabel> {title} </FormLabel>
</Stack>
<Collapse in={expanded}>
{filterOptions
? filterOptions?.map((item) => (
<FormControlLabel
control={
<Checkbox
onChange={(event, checked) =>
handleCheckboxChange(checked, title, item.label)
}
/>
}
label={item.label}
value={item.label}
/>
))
: filterGroups?.map((item) => (
<FilterOptionGroup
title={item.title}
filterOptions={item.filterOptions}
filterGroups={item.filterGroups}
handleCheckboxChange={handleCheckboxChange}
/>
))}
</Collapse>
</FormControl>
</Box>
);
}
And a "Types.ts" file:
export interface FilterGroup {
title: string;
filterOptions?: FilterOption[];
filterGroups?: FilterGroup[];
}
export interface FilterOption {
label: string;
}
Can the following class component be converted into a function component by any chance?
The main issue I'm having is inside the following cellsrenderer function
cellsrenderer: (row, columnfield, value, defaulthtml, columnproperties): string => {
axios
.get("api/personnels/"+value)
.then(response => {
this.setState({
createdByName: response.data.displayedValues
}, ()=> {
console.log('Inside axios response after setting the state to the name of the project creater')
})
}).catch(err => console.log(err));
return this.state.createdByName;
}
}
I am running into an issue of infinite loop because of setState re-rendering issue and want to avoid it by using useEffect() hook maybe if I could but since this is a class component, I am not able to proceed forward.
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import {FormikApp} from './forms/AddProjectForm'
import JqxGrid, {IGridProps, jqx} from 'jqwidgets-scripts/jqwidgets-react-tsx/jqxgrid';
import JqxButton from 'jqwidgets-scripts/jqwidgets-react-tsx/jqxbuttons'
import {RouteComponentProps} from 'react-router-dom'
import 'jqwidgets-scripts/jqwidgets/styles/jqx.base.css'
import 'jqwidgets-scripts/jqwidgets/styles/jqx.material.css'
import 'jqwidgets-scripts/jqwidgets/styles/jqx.arctic.css'
import {Dialog} from "primereact/dialog";
import {Button} from "primereact/button";
import {properties} from "../properties";
import {Card} from "primereact/card";
import axios from "axios";
import {Messages} from "primereact/messages";
import _ from 'lodash'
export interface IState extends IGridProps {
projects: [],
selectedProject: [],
createdByName :string,
addDialogVisible: boolean,
blazerId: string,
username: string,
selectedRowIndex: number,
deleteDialogVisible: boolean
}
class Projects extends React.PureComponent<RouteComponentProps<{}>, IState> {
private baseUrl = properties.baseUrlWs
private myGrid = React.createRef<JqxGrid>()
private messages = React.createRef<Messages>()
private editrow: number = -1;
constructor(props: RouteComponentProps) {
super(props);
this.selectionInfo = this.selectionInfo.bind(this)
this.gridOnSort = this.gridOnSort.bind(this);
const columns: IGridProps['columns'] = [
{ text: 'Project Name', datafield: 'name', width: 390 },
{ text: 'Project Description', datafield: 'description', width: 390 },
{ text: 'Owner Assigned', datafield: 'institutionId', width: 180,hidden:true },
{ text: 'Created By', datafield: 'createdBy',
cellsrenderer: (row, columnfield, value, defaulthtml, columnproperties): string => {
axios
.get("api/personnels/"+value)
.then(response => {
this.setState({
createdByName: response.data.displayedValues
}, ()=> {
console.log('Inside axios response after setting the state to the name of the project creater')
})
}).catch(err => console.log(err));
return this.state.createdByName;
}
}
]
const source:any = {
dataFields: [
{ name: 'id', type: 'long'},
{ name: 'name', type: 'string' },
{ name: 'description', type: 'string' },
{ name: 'url', type: 'string'},
{ name: 'irbProtocol', type: 'string'},
{ name: 'institutionId', type: 'long' },
{ name: 'projectType', type: 'string' },
{ name: 'priority', type: 'string'},
{ name: 'researchDataSetType', type: 'string'},
{ name: 'statusIndicatorId', type: 'long'},
{ name: 'createdBy', type: 'string' }
],
dataType: 'json',
root: 'projects',
sortColumn: 'name',
sortdirection: 'asc',
url: this.baseUrl + 'api/projects/search/getProjectsById',
data: {
value: ''
}
}
const dataAdapter:any = new jqx.dataAdapter(source,
{
autoBind: true,
downloadComplete: (data:any, status:any, xhr:any):void => {
// if (!source.totalrecords) {
source.totalrecords = parseInt(data['page'].totalElements);
// }
},
formatData: (data:any):any => {
data.page = data.pagenum
data.size = data.pagesize
if (data.sortdatafield && data.sortorder) {
data.sort = data.sortdatafield + ',' + data.sortorder;
}
return data;
},
loadError (xhr, status, error) {
throw new Error('Error occurred in getting Projects for user ' + error.toString());
}
}
);
this.state = {
projects: [],
selectedProject: [],
createdByName : '',
blazerId: '',
username: '',
addDialogVisible: false,
selectedRowIndex: null,
deleteDialogVisible: false,
columns: columns,
rendergridrows: (params: any): any[] => {
const data = params.data
return data;
},
source: dataAdapter,
};
}
setValueProperty = (data:any):any => {
if (this.state && this.state.blazerId) {
data.value = this.state.blazerId
}
}
private gridOnSort(event: any): void {
const sortinformation = event.args.sortinformation;
let sortdirection = sortinformation.sortdirection.ascending ? 'ascending' : 'descending';
if (!sortinformation.sortdirection.ascending && !sortinformation.sortdirection.descending) {
sortdirection = 'null';
}
this.myGrid.current.updatebounddata('sort')
};
selectionInfo = (event: any): void => {
const selection = this.myGrid.current.getrowdata(event.args.rowindex)
this.setState({
selectedProject: selection
}, () => {
console.log('pushing ' + this.state.selectedProject)
this.props.history.push({
pathname: '/project',
state: {
project: this.state.selectedProject,
blazerId: this.state.blazerId
}
})
});
}
componentDidMount() {
console.log('In Projects.componentDidMount....' + sessionStorage.getItem('loggedInUser'))
if (sessionStorage.getItem('loggedInUser') != null) {
const loggedInUser = JSON.parse(sessionStorage.getItem('loggedInUser') as string)
this.setState({ employeeId: loggedInUser.employeeId})
}
}
render() {
const defaultView = this.state.addDialogVisible ? null : (this.state.employeeId && !_.isEmpty(this.state.employeeId)) ? (
<div style={{width: '100%', margin: '0 auto', display: 'table'}}>
<JqxGrid
// #ts-ignore
ref={this.myGrid}
theme={'arctic'}
altrows={true}
width="100%"
autoheight={true}
source={this.state.source}
columns={this.state.columns}
pageable={true}
sortable={true}
onSort={this.gridOnSort}
pagesize={20}
virtualmode={true}
rendergridrows={this.state.rendergridrows}
showtoolbar={true}
rendertoolbar={this.state.rendertoolbar}
columnsresize={true}/>
</div>
) : null
return (
<div className="project-page-main">
<Messages ref={this.messages} style={{width: '100%', margin: 'auto' }}/>
<div className="content">
{defaultView}
</div>
</div>
);
}
}
export default Projects;
I am trying to follow this page sample:
https://ant.design/components/table/
The antd filtering sample to be precise
I have a column which I know its 2 possible values only.
I can see in the debugger that the handlechange event is executed, but after click OK in the filter, the table is not filtered as it should
My best guess I am missing something on the OnFilter event
import React, { Component } from 'react';
import { Table, Tag, Button} from 'antd';
import { adalApiFetch } from '../../adalConfig';
import Notification from '../../components/notification';
class ListPageTemplatesWithSelection extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
filteredInfo: null,
sortedInfo: null,
};
this.handleChange= this.handleChange.bind(this);
this.clearFilters= this.clearFilters.bind(this);
this.clearAll= this.clearAll.bind(this);
}
handleChange(pagination, filters, sorter){
console.log('Various parameters', pagination, filters, sorter);
this.setState({
filteredInfo: filters,
sortedInfo: sorter,
});
}
clearFilters(){
this.setState({ filteredInfo: null });
}
clearAll(){
this.setState({
filteredInfo: null,
sortedInfo: null,
});
}
fetchData = () => {
adalApiFetch(fetch, "/PageTemplates", {})
.then(response => response.json())
.then(responseJson => {
if (!this.isCancelled) {
const results= responseJson.map(row => ({
key: row.Id,
Name: row.Name,
SiteType: row.SiteType,
Tags: row.Tags
}))
this.setState({ data: results });
}
})
.catch(error => {
console.error(error);
});
};
componentDidMount(){
this.fetchData();
}
render(){
let { sortedInfo, filteredInfo } = this.state;
sortedInfo = sortedInfo || {};
filteredInfo = filteredInfo || {};
const columns = [
{
title: 'Id',
dataIndex: 'key',
key: 'key',
},
{
title: 'Name',
dataIndex: 'Name',
key: 'Name',
},
{
title: 'Site Type',
dataIndex: 'SiteType',
key: 'SiteType',
filters: [
{ text: 'Modern Team Site', value: 'Modern Team Site' },
{ text: 'CommunicationSite', value: 'CommunicationSite' },
],
filteredValue: filteredInfo.name || null,
onFilter: (value, record) => record.Tags.includes(value),
},{
title: 'Tags',
key: 'Tags',
dataIndex: 'Tags',
render: Tags => (
<span>
{Tags && Tags.map(tag => {
let color = tag.length > 5 ? 'geekblue' : 'green';
if (tag === 'loser') {
color = 'volcano';
}
return <Tag color={color} key={tag}>{tag.toUpperCase()}</Tag>;
})}
</span>
),
}
];
const rowSelection = {
selectedRowKeys: this.props.selectedRows,
onChange: (selectedRowKeys) => {
this.props.onRowSelect(selectedRowKeys);
}
};
return (
<div>
<Button onClick={this.clearFilters}>Clear filters</Button>
<Button onClick={this.clearAll}>Clear filters and sorters</Button>
<Table rowSelection={rowSelection} columns={columns} dataSource={this.state.data} onChange={this.handleChange} />
</div>
);
}
}
export default ListPageTemplatesWithSelection;
In your SiteType column, you have mistakenly set filteredValue prop to filteredInfo.name. But the filter is not on a name column, it is on SiteType column.
Change this line from:
filteredValue: filteredInfo.name || null,
To:
filteredValue: filteredInfo.SiteType || null,
And it should be fine.
You need execute the fetchData function in the handleChange function. Add filter params to ajax request. Just like this:
handleTableChange = (pagination, filters, sorter) => {
const pager = { ...this.state.pagination };
pager.current = pagination.current;
this.setState({
pagination: pager,
});
this.fetch({
results: pagination.pageSize,
page: pagination.current,
sortField: sorter.field,
sortOrder: sorter.order,
...filters,
});
}
I have something like this on React:
const CheckboxItems = (t) => [ // that t is just a global prop
{
checked: true,
value: 'itemsCancelled',
id: 'checkBoxItemsCancelled',
labelText: t('cancellations.checkBoxItemsCancelled'),
},
{
checked: true,
value: 'requestDate',
id: 'checkboxRequestDate',
labelText: t('cancellations.checkboxRequestDate'),
},
{
checked: true,
value: 'status',
id: 'checkboxStatus',
labelText: t('cancellations.checkboxStatus'),
},
{
checked: true,
value: 'requestedBy',
id: 'checkboxRequestedBy',
labelText: t('cancellations.checkboxRequestedBy'),
},
];
class TableToolbarComp extends React.Component {
state = {
items: CheckboxItems(),
};
onChange = (value, id, event) => {
const { columnsFilterHandler } = this.props;
this.setState(({ items }) => {
const item = items.slice().find(i => i.id === id);
if (item) {
item.checked = !item.checked;
columnsFilterHandler(id, item.value, item.checked);
return { items };
}
});
};
render() {
const { items } = this.state;
return(
<>
{items.map(item => (
<ToolbarOption key={item.id}>
<Checkbox
id={item.id}
labelText={item.labelText}
value={item.value}
checked={item.checked}
onChange={this.onChange}
/>
</ToolbarOption>
))}
</>
)
}
export default compose(
connect(
({ cancellations }) => ({
columnId: cancellations.columnId,
columnValue: cancellations.columnValue,
isChecked: cancellations.isChecked,
}),
dispatch => ({
columnsFilterHandler: (columnId, columnValue, isChecked) => {
dispatch(columnsFilterAction(columnId, columnValue, isChecked));
},
}),
),
)(translate()(TableToolbarComp));
That works very well and it is dispatching the data I would need to use later.
But I have a mess on the Redux part which is changing the state of all of the checkboxes at once and not separately as it should. So, once I uncheck one of the checkboxes the other 3 also get checked: false. I don't see this change to checked: false on the UI, only I see it on the Redux console in the browser.
This is what I have in the reducer
const initialState = {
checkboxes: [
{
checked: true,
value: 'itemsCancelled',
id: 'checkBoxItemsCancelled',
},
{
checked: true,
value: 'requestDate',
id: 'checkboxRequestDate',
},
{
checked: true,
value: 'status',
id: 'checkboxStatus',
},
{
checked: true,
value: 'requestedBy',
id: 'checkboxRequestedBy',
},
],
}
[ActionTypes.COLUMNS_FILTER](state, action) {
return initialState.checkboxes.map(checkbox => {
if (!checkbox.id === action.payload.id) {
return checkbox;
}
return {
...checkbox,
checked: action.payload.isChecked,
};
});
}
Action:
const columnsFilterAction = (columnId, columnValue, isChecked) => ({
type: ActionTypes.COLUMNS_FILTER,
payload: { columnId, columnValue, isChecked },
});
So all I need to know is what I have to do manage the state of those checkboxes on Redux as it working on React. As all I see is that when I toggle the checkboxes all of them reach the same state.
You have !checkbox.id === action.payload.id as your condition logic. As all of your checkbox IDs are 'truthy', then this !checkbox.id evaluates to false, and is the same as writing if(false === action.payload.id).
I suspect you meant to write: if(checkbox.id !== action.payload.id).
What you want to do is pass the id of the checkbox you want to toggle in an action. That's all you need in an action to toggle state. Then in the reducer you want to map over the current state and just return the checkbox for any that don't match the id passed in the action. When the id does match, return a new option spreading the current checkbox's properties into the new object and setting the checked property to it's opposite.
Given this action:
const TOGGLE_CHECKBOX = 'TOGGLE_CHECKBOX'
function toggleCheckbox(id) {
return {
type: TOGGLE_CHECKBOX,
id
}
}
Actions - Redux - Guide to actions and action creators provided by the author of Redux.
This reducer will do the job.
function checkboxReducer(state = [], action = {}) {
switch(action.type) {
case TOGGLE_CHECKBOX:
return state.map(checkbox => {
if (checkbox.id !== action.id) {
return checkbox;
}
return {
...checkbox,
checked: checkbox.isChecked ? false : true,
}
})
default:
return state;
}
}
Reducers - Redux - Guide to reducers and how to handle actions provided by the author of Redux.
Here is a working Code Sandbox to demonstrate it working. You can click the checkboxes to see them toggling as expected.