How to include menu options with custom icons? - unicode

The documentation for react native popup menu mentions how to create menu options with a checkmark
const CheckedOption = (props) => (
)
I want to create menu options with custom icons. I do not have the unicode value for those icons. I created a custom MenuOptionWithIcon component and wrapped the icon and the menu option inside a view.
export class MenuOptionWithIcon
extends React.Component<IMenuOptionProps, {}> {
public static defaultProps: Partial<IMenuOptionProps> = {
disabled: false,
};
public render() {
return (
<View style={PopupMenuStyleSheet.menuOptionWithIcon}>
{this.props.icon}
<MenuOption
{...this.props}
text={this.props.text}
onSelect={this.props.onSelect}
disabled={this.props.disabled}
/>
</View>
);
}
}
But I am not able to apply customStyles to these options now. I want to increase the padding of each of these options so that the tap target is increased. Is this the right way to create a custom menu option with an icon? Or is there a way to get unicode values for the icons that I need? Thanks!
EDIT:
Based on the suggestion in the answer below, I did the following but I now only see the text in my menu option. I don't see the Icon being displayed. onSelect works, text is displayed but the icon is not displayed.
const IconOption = (props) => (
<MenuOption {...props}>
<Icon name={props.iconName} size={30} />
{props.children}
</MenuOption>
);
<MenuOptions customStyles={MenuOptionStyles}>
<IconOption
iconName='md-bookmark'
onSelect={this.onSelectSave.bind(this)}
text={MenuOptionStrings.Save}
/>
</MenuOptions>

for the icons you need special font - use e.g. react-native-vector-icons and then use the same approach as for CheckedOption:
const IconOption = ({iconName, text, ...others}) => (
<MenuOption {...others} >
<Text>
<Icon name={iconName} />
{' ' + text}
</Text>
</MenuOption>
)

Related

Material UI Customized styled element with theme

I'm starting to use Material UI in my project.
I have created a theme with some basic definitions, using my css variables:
import { createTheme } from '#mui/material/styles';
import theme from './_my-variables.scss';
const muiTheme = createTheme({
palette: {
primary: {
main: theme['color-blue'], // 'color-blue' is my css variable
},
},
typography: {
fontFamily: theme['brand-font'], // 'brand-font' is my css variable
},
}
I also used ThemeProvider in my App layout:
export const App = (props) => {
return (<Provider store={store}>
<ThemeProvider theme={muiTheme}>
<ConnectedRouter history={historyRef.history}>
<Layout {...props} />
</ConnectedRouter>
</ThemeProvider>
</Provider>);
};
I have a page with MUI elements, and it works fine and takes the new primary color and the new font.
<Box>
<Paper>
<Checkbox color="primary" /> <!-- I can see my new color in the page! Yay! -->
some text
</Paper>
</Box>
Now I want to create a custom element - like <MyCustomizeBox> that will have a different properties, using my css variables (for example - a specific width, defined in my variables).
How do I define them in my theme and how to use it to customize a new element?
I prefer not to user classes () because I want to create a generic elements for reusing. Also I don't want to change all Boxes in my app - only the customized ones.
I tries to use "withStyles" but I was getting the default theme instead of my customized theme and I saw on Google that I'm not support to use both "withStyles" and theme together.
At last, found the answer:
import { styled } from '#mui/system';
const MyCustomizedPaper = styled(Paper)(({ theme }) => ({
width: theme.custom.myCustomWidthCssVar
}));
<MyCustomizedPaper ></MyCustomizedPaper >

Custom Components in a Custom Layout in react-admin e.g. appbar, sidebar doesnt' don't show the correct theme colours for all page types

What you were expecting:
When you are dynamically pick a theme object in a custom layout based on the URL/history in react-admin resource, the correct theme is used on the every page type e.g. List, Show, Create and Edit.
What happened instead:
When you go to the resource listing page, the correct theme is used. But when you then navigate to any of the create/show or edit pages, the theme seems to default back to the base / primary one.
Steps to reproduce:
Create Custom Layout file. Overwrite the Appbar and List component (with Listbase) with custom ones too. Although I'm not sure the appbar/listbase steps are necessary.
Use the custom Layout in the Admin component.
Create some resources.
Dynamically pick a theme based on the name of the Resouce (using history).
Navigate to the listing page of the resources. They display the specific theme correctly.
Navigate to the create/show/edit pages of the resources. These, for some reason, default back to the primary theme (or primary default colours).
Related code:
At the root level code..
<Admin
authProvider={AuthProvider({ awsConfig })}
dashboard={Dashboard}
dataProvider={graphQLProvider}
history={history}
layout={Layout}
title="Home"
loginPage={CustomLogin}
// logoutButton={LogoutButton}
>
<LMSResource projection={["details"]} name="Assigment" options={{label: 'Assignments'}} create={AssignmentCreate} list={AssignmentList}
title="Generic" show={AssignmentShow} edit={AssignmentEdit} icon={AssignmentIcon}/>
In the custom Layout file (I have an array of themes to choose from that is imported from another file)
const Layout = ({
children,
dashboard,
logout,
}) => {
const [resource, setResource] = useState('');
const history = useHistory();
useEffect(() => {
const newResource = history.location.pathname.replace(/^\/|\/$/g, '').toLowerCase();
if(newResource===''){
setResource('primary');
} else if (newResource !== resource) {
setResource(newResource);
}
}, [history.location.pathname]);
function getTheme(themes){
let result = ''
var i=0;
for(i = 0; i < themes.length; i++){
if(Object.keys(themes[i])[0] === resource){
result = themes[i][resource];
}
}
return (!result ? themes[0].primary : result );
}
const theme = createMuiTheme(getTheme(themes));
const useStyles = makeStyles(() => ({
...
}));
const classes = useStyles(theme);
const dispatch = useDispatch();
const open = useSelector(state => state.admin.ui.sidebarOpen);
return (
<ThemeProvider theme={theme}>
<CssBaseline />
<Grid container>
<Box className={classes.sidebar} color="grey.200" borderRight={1} item md={3} lg={2} component={Grid} display={{ xs: 'none', md: 'block' }}>
<Grid>
<Logo/>
<SchoolText color='textPrimary'/>
</Grid>
<TextField className={[classes.search,classes['search' + resource]]} id="outlined-basic" label="Search" variant="outlined" />
<Sidebar open={open} className={classes.sidebar}>
<Menu logout={logout} hasDashboard={!!dashboard} />
</Sidebar>
</Box>
<Box item component={Grid} xs={12} md={9} lg={10}>
<AppBar title={resource} logout={logout} />
<Box component={Grid} className={classes.breadcrumb} display={{ xs: 'none', md: 'block' }} borderTop={1}>
<Breadcrumbs/>
</Box>
<main>
<div className={classes.content}>
{children}
</div>
</main>
</Box>
<Notification />
</Grid>
</ThemeProvider>
);
}; export default Layout
I can post the List, Show, Create and Edit components if necessary, but I presume if one of resource components is receiving the right theme variables, then they all should?
To be clear, I mean the layout colours outside of the List, Edit, Create and Show components and in the actual Layout components i.e. the appbar background is the wrong colour, as is the sidebar etc. So how can they be right for List but wrong for Edit, Create and Show pages when it uses the same layout component for each in the resource? I don't think I have any way of controlling this?
Environment
React-admin version: 3.7.1
Last version that did not exhibit the issue (if applicable): n/a
React version: 17.0.2
Browser: all
Stack trace (in case of a JS error): n/a
Sooo. Don't mess with Regexs kids, they're dangerous. The issue was related to the different URL structure of create/edit/show vs list and therefore the theme matching didn't work..

Adding text or description to image component to pure-react-carousel

What is the appropriate way to add a description to each ? Currently I am adding a div under and I am unable to see the text.
Use Image component with tag prop and isBgImage set tot true.
<Slide index={i}>
<Image
tag='div'
isBgImage
src={someSrc}
>
<div className={descriptionClass}>Your description here</div>
</Image>
</Slide>
You can bypass the issue with a wrapper, without using isBgImage or children native props.
Create a wrapper component and use the prop children containing the text
Use the wrapper in the Slide component, pass the image's source as a prop
Add the children content
Use the position attritbute to style your children
It would look like this:
import React from 'react';
import { CarouselProvider, Slider, Slide, ButtonBack, ButtonNext, Image } from 'pure-react-carousel';
const BackgroundImage = (props) => {
return (
<>
{props.children}
<Image src={props.img} />
</>
)
};
const childrenStyle = {
position: "absolute",
left: "50%",
top: "50%",
transform: "translate(-50%, -50%)"
}; // to center the text for example
const Carousel = () => {
return(
<div className='home-banner'>
<CarouselProvider
naturalSlideWidth={100}
naturalSlideHeight={30}
totalSlides={4}
>
<Slider>
<Slide index={0}>
<BackgroundImage img={"your_image"} >
<div>Your children text</div>
</BackgroundImage>
</Slide>
// other slides here
</Slider>
<ButtonBack>Back</ButtonBack>
<ButtonNext>Next</ButtonNext>
</CarouselProvider>
</div>
)
};

react-select dropdown menu not clicking with protractor

I am trying to write an automation script with protractor that will select an element from a react-select dropdown menu. However, when I select the dropdown menu element and use .click(), the menu doesn't open. It seems that the react-select dropdown isn't responding to the .click() call. Is there another method to call on the react-select dropdown that will click the menu and open it?
Code for the dropdown menu.
<div className={ cx('select-container')} data-mr-ass='grey-info'>
<Select
classNamePrefix='info'
components={{
DropdownIndicator: () => <Icon className={ cx('icon-wrap') } iconName={iconNames.downArrow} />,
Option: (props) => <div data-mr-ass='grey-info-options'> <components.Option {...props}/></div> }}
isSearchable={false}
onChange={selectGrayPercent}
options={grayOptions}
placeholder='Select'
value={grayValue}
openMenuOnClick={true}
/>
</div>
</div>
I tried doing $(attr('grey-info')).click(); but the menu doesn't open so I am unable to select any of the options in the menu. data-mr is my div class name to select elements using Protractor.
you can now use protractor-react-selector to identify web elements by react's component, props, and state.
You can identify your target element by:
const myElement = element(
by.react('Select', { someBooleanProp: true }, { someBooleanState: true })
);
Let me know if this helps!

How to change expansion panel icon position to the left?

In my app, the expansion arrow has to be in the left side of the panel.
But, by default it's displaying in the right side.
This :
<ExpansionPanelSummary
className={classes.panelSummary}
expandIcon={<ExpandMoreIcon />}
IconButtonProps={{edge: 'start'}}
aria-controls='panel1a-content'
id='panel1a-header'
>
Doesn't made it.
Granted, you can't (easily) change the order in which the components appear in the HTML. However, there is a way using only CSS. ExpansionPanelSummary uses display: flex; you can therefore set the order property on the icon to make it appear to the left of the content.
This can be achieved with either useStyles or withStyles (Or possibly using plain CSS, but I haven't tried it); here's how you'd go about using the latter:
import withStyles from "#material-ui/core/styles/withStyles";
const IconLeftExpansionPanelSummary = withStyles({
expandIcon: {
order: -1
}
})(ExpansionPanelSummary);
You can then write the rest of your code using IconLeftExpansionPanelSummary instead of ExpansionPanelSummary when you want the icon to appear to the left. Don't forget to set IconButtonProps={{edge: 'start'}} on the component for proper spacing.
<AccordionSummary
className={classes.accordionSummary}
classes={{
expandIcon: classes.expandIcon,
expanded: classes.expanded
}}
IconButtonProps={{
disableRipple: true
}}
></AccordionSummary>
You can add class and use flex-direction
accordionSummary: {
flexDirection: 'row-reverse'
}
It's simple
add class on <ExpansionPanelSummary> like this
<ExpansionPanelSummary className={classes.panelSummary}>
add css against this class in jss like this
panelSummary:{flexDirection: "row-reverse"},
In case using css
add class on <ExpansionPanelSummary> like this
<ExpansionPanelSummary className="panelSummary">
add css against this class in jss like this
.panelSummary{flex-direction: row-reverse;}
you can get the expansion panel icon on left by removing it from expandIcon and add it as a children in Summary something like this
<ExpansionPanel defaultExpanded={true}>
<ExpansionPanelSummary aria-controls="panel1a-content">
{this.state.expanded ? <RemoveIcon/> : <ExpandIcon />}
<Typography component='h4' variant='h4'>My Expansion Panel</Typography>
</ExpansionPanelSummary>
<ExpansionPanelsDetails />
</ExpansionPanel>
The challenge is that the order is hardcoded into the codebase and you will not be able to use the ExpansionPanel as is.
If you look at the implementation, you will find the code as below
<div className={clsx(classes.content, { [classes.expanded]: expanded })}>{children}</div>
{expandIcon && (
<IconButton
disabled={disabled}
className={clsx(classes.expandIcon, {
[classes.expanded]: expanded,
})}
edge="end"
component="div"
tabIndex={-1}
aria-hidden
{...IconButtonProps}
>
{expandIcon}
</IconButton>
)}
As you see the <div> contains the text and then the IconButton is displayed.
So, you may have to work with what's provided out of the box or create your own Component based on what material-UI provides.
Hope that helps.
You can modify the CSS class like this:
notice the absolute position, in this way you can move the div that contains the icon whatever position you want with 'left' or 'right' properties
const useStyles = makeStyles((theme) => ({
ExpansionPanelSummaryExpandedIcon: {
'& div.MuiExpansionPanelSummary-expandIcon': {
position: 'absolute',
right: '5%',
},
}
}));
and then use in the ExpansionPanelSummary
<ExpansionPanelSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="panel1-content"
id="panel1bh-header"
className={classes.ExpansionPanelSummaryExpandedIcon}
>
references:
https://cssinjs.org/?v=v10.3.0
https://v4-8-3.material-ui.com/customization/components/#overriding-styles-with-classes