Tabs navigation bar hides elements beneath it - reactjs

I'm using material-ui to write a navigation bar by using Tab. My tab navigation bar is almost the same as Master.jsx:
import React from 'react';
import {
AppCanvas,
IconButton,
EnhancedButton,
Mixins,
Styles,
Tab,
Tabs,
Paper} from 'material-ui';
const {StylePropable} = Mixins;
const {Colors, Spacing, Typography} = Styles;
const ThemeManager = Styles.ThemeManager;
const DefaultRawTheme = Styles.LightRawTheme;
const Master = React.createClass({
mixins: [StylePropable],
getInitialState() {
let muiTheme = ThemeManager.getMuiTheme(DefaultRawTheme);
// To switch to RTL...
// muiTheme.isRtl = true;
return {
muiTheme,
};
},
propTypes: {
children: React.PropTypes.node,
history: React.PropTypes.object,
location: React.PropTypes.object,
},
childContextTypes: {
muiTheme: React.PropTypes.object,
},
getChildContext() {
return {
muiTheme: this.state.muiTheme,
};
},
getStyles() {
let darkWhite = Colors.darkWhite;
return {
footer: {
backgroundColor: Colors.grey900,
textAlign: 'center',
},
a: {
color: darkWhite,
},
p: {
margin: '0 auto',
padding: 0,
color: Colors.lightWhite,
maxWidth: 335,
},
github: {
position: 'fixed',
right: Spacing.desktopGutter / 2,
top: 8,
zIndex: 5,
color: 'white',
},
iconButton: {
color: darkWhite,
},
};
},
componentWillMount() {
let newMuiTheme = this.state.muiTheme;
newMuiTheme.inkBar.backgroundColor = Colors.yellow200;
this.setState({
muiTheme: newMuiTheme,
tabIndex: this._getSelectedIndex()});
let setTabsState = function() {
this.setState({renderTabs: !(document.body.clientWidth <= 647)});
}.bind(this);
setTabsState();
window.onresize = setTabsState;
},
componentWillReceiveProps(nextProps, nextContext) {
let newMuiTheme = nextContext.muiTheme ? nextContext.muiTheme : this.state.muiTheme;
this.setState({
tabIndex: this._getSelectedIndex(),
muiTheme: newMuiTheme,
});
},
render() {
let styles = this.getStyles();
let githubButton = (
<IconButton
iconStyle={styles.iconButton}
iconClassName="muidocs-icon-custom-github"
href="https://github.com/callemall/material-ui"
linkButton={true}
style={styles.github} />
);
let githubButton2 = (
<IconButton
iconStyle={styles.iconButton}
iconClassName="muidocs-icon-custom-github"
href="https://github.com/callemall/material-ui"
linkButton={true}/>
);
return (
<AppCanvas>
{githubButton}
{ this._getTabs() }
{this.props.children}
</AppCanvas>
);
},
_getTabs() {
let styles = {
root: {
backgroundColor: Colors.cyan500,
position: 'fixed',
height: 64,
top: 0,
right: 0,
zIndex: 1101,
width: '100%',
},
container: {
position: 'absolute',
right: (Spacing.desktopGutter / 2) + 48,
bottom: 0,
},
span: {
color: Colors.white,
fontWeight: Typography.fontWeightLight,
left: 45,
top: 22,
position: 'absolute',
fontSize: 26,
},
svgLogoContainer: {
position: 'fixed',
width: 300,
left: Spacing.desktopGutter,
},
svgLogo: {
width: 65,
backgroundColor: Colors.cyan500,
position: 'absolute',
top: 20,
},
tabs: {
width: 425,
bottom: 0,
},
tab: {
height: 64,
},
};
const materialIcon =
<EnhancedButton
style={styles.svgLogoContainer}
linkButton={true}
href="/#/home">
<img style={this.prepareStyles(styles.svgLogo)} src="images/material-ui-logo.svg"/>
<span style={this.prepareStyles(styles.span)}>material ui</span>
</EnhancedButton>
return (
<div>
<Paper
zDepth={0}
rounded={false}
style={styles.root}>
{materialIcon}
<div style={this.prepareStyles(styles.container)}>
<Tabs
style={styles.tabs}
value={this.state.tabIndex}
onChange={this._handleTabChange}>
<Tab
value="1"
label="GETTING STARTED"
style={styles.tab}
route="/get-started" />
<Tab
value="2"
label="CUSTOMIZATION"
style={styles.tab}
route="/customization"/>
<Tab
value="3"
label="COMPONENTS"
style={styles.tab}
route="/components"/>
</Tabs>
</div>
</Paper>
</div>
);
},
_getSelectedIndex() {
return this.props.history.isActive('/get-started') ? '1' :
this.props.history.isActive('/customization') ? '2' :
this.props.history.isActive('/components') ? '3' : '0';
},
_handleTabChange(value, e, tab) {
this.props.history.pushState(null, tab.props.route);
this.setState({tabIndex: this._getSelectedIndex()});
},
});
export default Master;
Basically I removed AppBar, AppLeftNav and FullWidthSection.
The only problem is that the Tabs hides some elements beneath it, see the picture below:
I must did something wrong, any ideas? Thanks!

Your root style is fixed. It will cause the element to stick on the top. Remove it.

OK, I found paddingTop: Spacing.desktopKeylineIncrement + 'px', in page-with-nav.jsx, which is the right solution.
The reason the elements beneath the navigation tab is covered is because fixed position elements are removed from the document flow and do not take up any space. So the elements are beginning at the top as if the header isn't there. What you have to do is use padding or margin to take up the space that would have been occupied by your header if it was in the normal flow.

Related

React component not passing value to correct component

I have a simple React app that uses react-dnd to build a grid of draggable squares that have an initial color and text value and two components to change the color and change the text.
The color change (via ColorPicker2, using react-color library) works okay. The text change (using TextInput from #carbon/react) doesn't work as desired.
I thought I was applying the same logic with both components, but whilst the color-picker updates the color and retains that color when the square is moved, the text seems to render inside the TextInput and not the square itself and I can't figure out the logical difference.
The Code Sandbox is here: https://codesandbox.io/s/wild-waterfall-z8s1de?file=/src/App.js
This is the current code:
ColorPicker2.js
import "./styles.css";
import React from "react";
import { BlockPicker } from "react-color";
const presetColors = ["#9E9E9E", "#4CAF50", "#FFEB3B", "#F44336", "#2196F3"];
const ColorPicker2 = (props) => {
const handleChangeComplete = (color) => {
if (props.updateColor) {
props.updateColor(color.hex);
}
};
return (
<div className="palette">
<BlockPicker
className="palette"
colors={presetColors}
onChangeComplete={handleChangeComplete}
presetColors={Object.values(presetColors)}
color={props.currentColor}
/>
</div>
);
};
export default ColorPicker2;
App.js
import "./styles.css";
import React, { useState } from "react";
import { DndProvider, useDrag, useDrop } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";
import edit from "./edit.svg";
import palette from "./palette.svg";
import ColorPicker2 from "./ColorPicker2";
import { TextInput } from "#carbon/react";
const DndWrapper = (props) => {
return <DndProvider backend={HTML5Backend}>{props.children}</DndProvider>;
};
const DraggableSquare = ({ index, text, color, moveSquare }) => {
const [{ isDragging }, drag] = useDrag({
type: "square",
item: { index },
collect: (monitor) => ({
isDragging: monitor.isDragging()
})
});
const [isHovered, setIsHovered] = useState(false);
const [, drop2] = useDrop({
accept: "square",
drop: (item, monitor) => {
const didDrop = monitor.didDrop();
if (!didDrop) {
moveSquare(item.index, index);
}
},
hover: (item, monitor) => {
setIsHovered(monitor.isOver());
},
collect: (monitor) => {
setIsHovered(monitor.isOver());
}
});
const [isPaletteOpen, setIsPaletteOpen] = useState(false);
const [isTextInputOpen, setIsTextInputOpen] = useState(false);
const [newText, setNewText] = useState(text);
const opacity = isDragging ? 0.5 : 1;
return (
<div className="square-div" ref={drop2}>
<div
className="grey-square"
ref={drag}
style={{
opacity,
backgroundColor: color,
width: "200px",
height: "200px",
textAlign: "center",
paddingTop: "30px",
position: "relative",
border: isHovered ? "3px solid blue" : "none",
borderRadius: "5px",
}}
onMouseOver={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
<img
src={edit}
onClick={() => {
setIsTextInputOpen(!isTextInputOpen);
if (!isTextInputOpen) {
moveSquare(index, newText, color, undefined);
}
}}
style={{
width: "15px",
height: "15px",
position: "absolute",
right: "5px",
top: "5px"
}}
alt="edit icon"
/>
{isTextInputOpen && (
<TextInput
id="newtext"
labelText=""
value={newText}
onChange={(e) => setNewText(e.target.value)}
/>
)}
<img
src={palette}
onClick={() => setIsPaletteOpen(!isPaletteOpen)}
style={{
width: "15px",
height: "15px",
position: "absolute",
right: "25px",
top: "5px"
}}
alt="palette icon"
/>
{isPaletteOpen && (
<ColorPicker2
className="palette"
currentColor={color}
updateColor={(newColor) =>
moveSquare(index, index, newText, newColor)
}
/>
)}
</div>
</div>
);
};
const Grid = () => {
const [grid, setGrid] = useState([
{ text: "1", color: "grey" },
{ text: "2", color: "grey" },
{ text: "3", color: "grey" },
{ text: "4", color: "grey" },
{ text: "5", color: "grey" },
{ text: "6", color: "grey" },
{ text: "7", color: "grey" },
{ text: "8", color: "grey" },
{ text: "9", color: "grey" },
{ text: "10", color: "grey" },
{ text: "11", color: "grey" },
{ text: "12", color: "grey" },
{ text: "13", color: "grey" },
{ text: "14", color: "grey" },
{ text: "15", color: "grey" }
]);
const moveSquare = (fromIndex, toIndex, newText, newColor) => {
setGrid((grid) => {
const newGrid = [...grid];
const item = newGrid[fromIndex];
newGrid.splice(fromIndex, 1);
newGrid.splice(toIndex, 0, {
text: newText || item.text,
color: newColor || item.color
});
return newGrid;
});
};
return (
<>
<DndWrapper>
<div
className="grid"
style={{
display: "grid",
gridTemplateColumns: "repeat(5, 190px)",
gridGap: "15px",
gridColumnGap: "20px",
gridRowGap: "10px",
position: "absolute"
}}
>
{grid.map((square, index) => (
<DraggableSquare
key={index}
index={index}
text={square.text}
color={square.color}
moveSquare={moveSquare}
//grid={grid}
//setGrid={setGrid}
/>
))}
</div>
</DndWrapper>
</>
);
};
export default Grid;
Any thoughts from fresh eyes would be helpful.
I think this is just a simple issue with using index as the key while mapping. Adjusting your code pen to have a unique key fixed it for me but the input text is not being saved anywhere so returned to the default text={square.text} when moved as expected.
Unique Id in objects:
const [grid, setGrid] = useState([
{ text: "1", color: "grey", id: crypto.randomUUID() },
{ text: "2", color: "grey", id: crypto.randomUUID() },
{ text: "3", color: "grey", id: crypto.randomUUID() },...])
Adding key to mapped object:
{grid.map((square, index) => (
<DraggableSquare
key={square.id}
index={index}
text={square.text}
color={square.color}
moveSquare={moveSquare}
//grid={grid}
//setGrid={setGrid}
/>}

react The swipe animation will be performed twice

When you press the Circle button, the Box moves to the right and disappears from the screen.
Additional information (FW/tool version, etc.)
react
scss
Typescript
framer-motion
import "./style.scss";
import React, { FunctionComponent, useState } from "react";
import { useMotionValue, useTransform } from "framer-motion";
import { SwipeBox } from "./SwipeBox";
const App: FunctionComponent = () => {
const [cards, setCards] = useState([
const onClic = () => {
animateCardSwipe({ x: -1400, y: 0 });
}; <div
style={{
width: "400px",
height: "300px",
background: `${card.background}`
}}
>
) : (
</div>
);
};
export default App;
The problem is that the animation is happening on mount and you're updating state twice inside the animateCardSwipe function:
const animateCardSwipe = (animation: { x: number, y: number }) => {
setAnime({ ...anime, animation });
setTimeout(() => {
x.set(0);
y.set(0);
setCards([...cards.slice(0, cards.length - 1)]);
}, 200);
};
I personally like to use a more imperative approach here for starting the animation using animate:
const animateCardSwipe = (animation: { x: number, y: number }) => {
animate(x, animation.x, {
duration: 1,
onComplete: () => {
x.set(0);
y.set(0);
setCards([...cards.slice(0, cards.length - 1)]);
},
});
};
This implementation also waits for the animation to complete before rearranging the cards.
Full example:
import React, { FunctionComponent, useState } from "react";
import {
animate,
useAnimation,
useMotionValue,
useTransform,
MotionValue,
motion,
} from "framer-motion";
interface Props {
animate?: { x: number; y: number };
style: {
x?: MotionValue;
y?: MotionValue;
zIndex: number;
rotate?: MotionValue;
};
card: { text: string; background: string };
}
const SwipeBox: React.FunctionComponent<Props> = (props) => {
return (
<motion.div
animate={props.animate}
className="card"
style={{
...props.style,
background: "white",
borderRadius: "8px",
display: "grid",
top: 0,
left: 0,
placeItems: "center center",
position: "absolute",
width: "100%",
}}
transition={{ duration: 1 }}
>
{props.children}
</motion.div>
);
};
const App: FunctionComponent = () => {
const controls = useAnimation();
console.log(controls);
const [cards, setCards] = useState([
{ text: "Up or down", background: "red" },
{ text: "Left or right", background: "green" },
{ text: "Swipe me!", background: "gray" },
{ text: "Swipe me!", background: "purple" },
{ text: "Swipe me!", background: "yellow" },
]);
const x = useMotionValue(0);
const y = useMotionValue(0);
const animateCardSwipe = (animation: { x: number; y: number }) => {
animate(x, animation.x, {
duration: 1,
onComplete: () => {
x.set(0);
y.set(0);
setCards([...cards.slice(0, cards.length - 1)]);
},
});
};
const [anime, setAnime] = useState<{ animation: { x: number; y: number } }>({
animation: { x: 0, y: 0 },
});
const rotate = useTransform(x, [-950, 950], [-30, 30]);
const onClickLeft = () => {
animateCardSwipe({ x: 1400, y: 0 });
};
const onClickRight = () => {
animateCardSwipe({ x: -1400, y: 0 });
};
return (
<div style={{ display: "flex", flexDirection: "column" }}>
<div>
{cards.map((card, index) =>
index === cards.length - 1 ? (
<SwipeBox
animate={anime.animation}
card={card}
key={index}
style={{ x, y, zIndex: index, rotate: rotate }}
>
<div
style={{
width: "400px",
height: "300px",
background: `${card.background}`,
}}
>
{card.text}
</div>
</SwipeBox>
) : (
<SwipeBox
card={card}
key={index}
style={{
zIndex: index,
}}
>
<div
style={{
width: "400px",
height: "300px",
background: `${card.background}`,
}}
>
{card.text}
</div>
</SwipeBox>
)
)}
</div>
<div style={{ zIndex: 999 }}>
<button onClick={onClickRight}>✖️</button>
<button onClick={onClickLeft}>○</button>
</div>
</div>
);
};
export default App;
Codesandbox Demo

accessing this.props.navigation.state.params ouside render

I am passing a parameter from ListView to a detailed page. Can I access this parameter outside the render on my detailed page:
Below is my code to pass the parameter:
<ListView
dataSource={this.state.dataSource}
renderRow = {( rowData ) =>
<TouchableOpacity style = { styles.item } activeOpacity = { 0.4 } onPress = { this.clickedItemText.bind( this, rowData ) }>
<Text style = { styles.text }>{ rowData.ser }</Text>
</TouchableOpacity>
}
renderSeparator = {() =>
<View style = { styles.separator }/>
}
enableEmptySections = { true }
/>
</View>
clickedItemText( clickedItem )
{
this.props.navigation.navigate('Item', { item: clickedItem });
}
I can get the parameter value on my detailed page using the code below:
<View style = { styles.container2 }>
<Text style = { styles.text }>You Selected: { this.props.navigation.state.params.item.Location.toUpperCase() }</Text>
</View>
I need to do this.props.navigation.state.params.item.Location ouside render. The reason I want to do that because I want to create a ListView on my detailed page by filtering my Json data based on the passed parameter so for e.g. if the parameter passed is 2 then I want to filter my JSON data based on 2 and create another ListView.
As far as I know filtering of the JSON data can only be done outside render. I could be wrong though, I am new to react Native.
Below is my entire class for detailed page.
import React, { Component } from 'react';
import { StyleSheet, Text, View, ListView, ActivityIndicator, TextInput } from 'react-native';
import ServiceDetails from '../reducers/ServiceDetails';
class ServiceListDetails extends Component
{
constructor() {
super();
var newList = ServiceDetails.filter(obj => obj.fk === this.props.navigation.state.params.item.id);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
dataSource: ds.cloneWithRows(ServiceDetails),
};
}
static navigationOptions =
{
title: 'SecondActivity',
};
ListViewItemSeparator = () => {
return (
<View
style={{
height: .5,
width: "100%",
backgroundColor: "#000",
}}
/>
);
}
render()
{
return(
<View style={styles.MainContainer}>
<ListView
dataSource={this.state.dataSource}
renderSeparator= {this.ListViewItemSeparator}
renderRow={(rowData) => <Text>{rowData.addr}</Text>}
/>
</View>
);
}
}
const styles = StyleSheet.create(
{
MainContainer:
{
justifyContent: 'center',
flex:1,
margin: 10
},
TextStyle:
{
fontSize: 23,
textAlign: 'center',
color: '#000',
},
rowViewContainer:
{
fontSize: 17,
paddingRight: 10,
paddingTop: 10,
paddingBottom: 10,
},
ActivityIndicator_Style:
{
flex: 1,
alignItems: 'center',
justifyContent: 'center',
left: 0,
right: 0,
top: 0,
bottom: 0,
},
TextInputStyleClass:{
textAlign: 'center',
height: 40,
borderWidth: 1,
borderColor: '#009688',
borderRadius: 7 ,
backgroundColor : "#FFFFFF"
}
});
export default ServiceListDetails;
Below is screen shot of the error that I am getting on detailed page.
Below is the entire code of my Master Page that has a list View on it and it works fine:
import React, { Component } from 'react';
import { Text, View, StyleSheet, ListView, ActivityIndicator, TextInput, TouchableOpacity } from 'react-native';
import { Provider, connect } from 'react-redux';
import { createStore } from 'redux'
import reducers from '../reducers/ServiceReducer';
import ServiceItem from './ServiceItem';
import Icon from 'react-native-vector-icons/EvilIcons';
import ServiceDetail from './ServiceDetail';
import TestActivity from './TestActivity';
import { StackNavigator } from 'react-navigation';
import ServiceListDetails from './ServiceListDetails' ;
class AutoCompActivity extends Component {
constructor(props) {
super(props);
this.state = {
// Default Value of this State.
Loading_Activity_Indicator: true,
text:'',
}
this.arrayholder=[];
}
componentDidMount() {
const data = require('../reducers/services.json')
let ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.setState({
Loading_Activity_Indicator: false,
dataSource: ds.cloneWithRows(data),
}, function() {
// In this block you can do something with new state.
this.arrayholder = data ;
});
}
SearchFilterFunction(text){
const newData = this.arrayholder.filter(function(item){
const itemData = item.ser.toUpperCase()
const textData = text.toUpperCase()
return itemData.indexOf(textData) > -1
})
this.setState({
dataSource: this.state.dataSource.cloneWithRows(newData),
text: text
})
}
ListViewItemSeparator = () => {
return (
<View
style={{
height: .5,
width: "100%",
backgroundColor: "#000",
}}
/>
);
}
/*Navigate_To_Second_Activity=(ser)=>
{
//Sending the JSON ListView Selected Item Value On Next Activity.
this.props.navigation.navigate('Second', { JSON_ListView_Clicked_Item: ser });
}*/
clickedItemText( clickedItem )
{
this.props.navigation.navigate('Item', { item: clickedItem });
}
static navigationOptions =
{
title: 'MainActivity',
};
render()
{
if (this.state.Loading_Activity_Indicator) {
return (
<View style={styles.ActivityIndicator_Style}>
<ActivityIndicator size = "large" color="#009688"/>
</View>
);
}
return (
<View style={styles.MainContainer}>
<TextInput
style={styles.TextInputStyleClass}
onChangeText={(text) => this.SearchFilterFunction(text)}
value={this.state.text}
underlineColorAndroid='transparent'
placeholder="Search Here"
/>
<ListView
dataSource={this.state.dataSource}
renderRow = {( rowData ) =>
<TouchableOpacity style = { styles.item } activeOpacity = { 0.4 } onPress = { this.clickedItemText.bind( this, rowData ) }>
<Text style = { styles.text }>{ rowData.ser }</Text>
</TouchableOpacity>
}
renderSeparator = {() =>
<View style = { styles.separator }/>
}
enableEmptySections = { true }
/>
</View>
);
}
}
export default MyNewProject= StackNavigator(
{
First: {screen: AutoCompActivity},
Item: {screen: ServiceListDetails}
}
);
const styles = StyleSheet.create(
{
MainContainer:
{
justifyContent: 'center',
flex:1,
margin: 10
},
TextStyle:
{
fontSize: 23,
textAlign: 'center',
color: '#000',
},
rowViewContainer:
{
fontSize: 17,
paddingRight: 10,
paddingTop: 10,
paddingBottom: 10,
},
ActivityIndicator_Style:
{
flex: 1,
alignItems: 'center',
justifyContent: 'center',
left: 0,
right: 0,
top: 0,
bottom: 0,
},
TextInputStyleClass:{
textAlign: 'center',
height: 40,
borderWidth: 1,
borderColor: '#009688',
borderRadius: 7 ,
backgroundColor : "#FFFFFF"
},
separator:
{
height: 2,
backgroundColor: 'rgba(0,0,0,0.5)'
},
item:
{
padding: 15
},
text:
{
fontSize: 18
},
});
My services.json is below
[
{
"id":0,
"ser": "Test Service",
"Location": "TestLoc",
"Phone1":"(999)-999-5050",
"SecondLoc": "TestLoc2",
"email":"test#test.com",
"sourceLat":"33.977806",
"sourceLong":"-117.373261",
"destLatL1":"33.613355",
"destLongL1":"-114.596569",
"destLatL2":"33.761693",
"destLongL2":"-116.971169",
"destAddr1": "Test Address, Test Drive",
"destAddr2": "Test Address2, Test Drive2",
"onlineURL":"",
"Phone2": "(900)-900-3333"
}
]
My ServiceDetails.json is below:
[
{
"id":"1",
"fk": "0",
"addr": "2Test addr",
"phone": "(951)-955-6200",
"LatL":"33.935547",
"Long2":"-117.191",
"Online": ""
},
{
"id":"2",
"fk": "0",
"addr": "testaddr21",
"phone": "(999)-999-9999",
"LatL":"33.977880",
"Long2":"-117.1234",
"Online": ""
}
]
How can I achieve this?

React Component wont render in Codepen

I have a simple radio button group component on codepen here that is not rendering in codepen. I want to post this to the code review stackexchange, since it is one of the first components i've built and will be necessary in many places on a web app I am building. However for that post, I want my codepen example to be working.
I think I am probably breaking some rule about how to use es6 in react to get the app to render, but I am struggling to debug. My console.logs() are not helping, and the error messages in codepen arent helping a ton either.
Since I linked to my codepen, I have to accompany it with code, so here's what I have in my codepen at the moment:
import React, { Component } from 'react';
import { ToggleButton, ToggleButtonGroup } from 'react-bootstrap';
class ToolButtonGroup extends Component {
constructor(props) {
super(props);
};
render() {
// Get Variables from the params prop
const { header, buttons, initialVal } = this.props.params;
const { borderRadius, margin, padding, fontsize, border } = this.props.params;
const { gridID, gridColumns, minRowHeight } = this.props.params;
// Create the individual buttons
const pageButtons = buttons.map((buttoninfo, idx) => {
return (
<ToggleButton
key={idx}
style={{
"borderRadius": borderRadius,
"margin": margin,
"padding": padding,
"fontSize": fontsize,
"border": border
}}
bsSize="large"
value={buttoninfo.value}>
{buttoninfo.label}
</ToggleButton>
)
})
// Return the button group
return(
<div
style={{"border": "1px solid red" }}
id={gridID}>
<h2 style={{
"width": "100%",
"margin": "0 auto",
"fontSize": "1.75em",
"marginTop": "5px",
"border": "none"
}}
>{header}</h2>
<ToggleButtonGroup
type="radio"
name="charttype-options"
defaultValue={initialVal}
onChange={this.props.handler}
style={{
"display": "grid",
"gridTemplateColumns": "repeat(" + gridColumns + ", 1fr)",
"gridAutoRows": "auto",
"gridGap": "8px"
}}
>
{pageButtons}
</ToggleButtonGroup>
</div>
)
}
}
class StarterApp extends Component {
constructor(props){
super(props);
this.state = {
pitchersOrHitters: "",
position: ""
}
}
// Button and Select Handlers!
handlePitchHitChange = (pitchersOrHitters) => {
this.setState({pitchersOrHitters})
}
handlePositionChange = (position) => {
this.setState({ position: position });
}
render() {
console.log("A")
// 0. Load State and Props
const { pitchersOrHitters, position } = this.state;
// Pitcher or Hitter Radio Button Group params
const pitchOrHitButtonGroup = {
borderRadius: "25px",
margin: "1% 10%",
padding: "5%",
fontsize: "2em",
border: "2px solid #BBB",
gridColumns: 1, minRowHeight: "10px", "gridID": "buttons1",
header: "Choose One:",
buttons: [
{ value: "Pitchers", label: "Pitchers" },
{ value: "Hitters", label: "Hitters" },
],
initialVal: "Pitchers"}
// Pitcher or Hitter Radio Button Group params
const positionButtonGroup = {
borderRadius: "10px",
margin: "1% 10%",
padding: "5%",
fontsize: "1.25em",
border: "2px solid #BBB",
gridColumns: 4, minRowHeight: "20px", "gridID": "buttons2",
header: "Choose One:",
buttons: [
{ value: "SP", label: "SP" },
{ value: "RP", label: "RP" },
{ value: "1B", label: "1B" },
{ value: "2B", label: "2B" },
{ value: "SS", label: "SS" },
{ value: "3B", label: "3B" },
{ value: "LF", label: "LF" },
{ value: "RF", label: "RF" },
{ value: "CF", label: "CF" }
],
initialVal: "SP"}
return(
<div className="chart-grid-container">
<ToolButtonGroup
params={pitchOrHitButtonGroup}
value={pitchersOrHitters}
handler={this.handlePitchHitChange} />
<ToolButtonGroup
params={positionButtonGroup}
value={position}
handler={this.handlePositionChange} />
</div>
)
}
}
ReactDOM.render(
<StarterApp />,
document.getElementById('root')
);
.chart-grid-container {
display: grid;
grid-template-columns: repeat(12, 1fr);
grid-auto-rows: minmax(200px, auto);
grid-gap: 5px;
grid-template-areas:
"btns1 btns1 btns2 btns2 btns2 btns2 . . . . . .";
}
#buttons1 { grid-area: btns1; }
#buttons2 { grid-area: btns2; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.1/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.1/react-dom.min.js"></script>
<div id='root'>
COME ON WORK!
</div>
Im unsurprisingly struggling to get this code snippet working as well. Although in this case, it is because I don't know how to include react-bootstrap, which is something I've already done in my codepen.
Thanks!
I noticed I got the errors when using import statements on that specific project.
This is probably a limitation of transpiling engine on the codepen. Better if you use some platform (ie: enter link description here) that already has all of these solved out for you.
Here is your code on codesandbox.

Update React state from outside render when using HOC?

I'm creating a React widget and using the react-jss HOC for styling. The widget is just a small part of a larger page, and I want to be able to signal it to open or close with a button on another part of the page outside of the React render. Originally I was doing it like this:
var modal = ReactDOM.render(<Modal />, document.getElementById('widget'))
// Inside an onClick function
modal.toggleModal()
That was before JSS, but now widget doesn't return the component, it returns the JSS HOC. I've tried passing <Widget /> a prop and updating that and then using widget.forceUpdate() but that did nothing. Not really sure what else to try at this point. I'm currently just toggling everything outside of React, but I want the component to be able to close itself as well.
import React, { Component } from 'react'
import injectSheet from 'react-jss'
const styles = {
show: {
display: 'block',
},
modal: {
display: 'none',
background: 'rgba(0, 0, 0, 0.3)',
position: 'fixed',
top: 0,
left: 0,
bottom: 0,
right: 0,
width: '100%',
height: '100%',
},
form: {
maxWidth: '440px',
margin: '15px',
padding: '30px',
background: '#fff',
position: 'fixed',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)'
},
input: {
width: '100%',
marginBottom: '15px'
},
button: {
width: '100%'
}
}
class Modal extends Component {
constructor(props) {
super(props)
this.state = {
show: false
}
this.toggleModal = this.toggleModal.bind(this)
}
toggleModal() {
this.setState({show: !this.state.show})
}
render() {
const { classes } = this.props
return (
<div className={`${classes.modal} ${this.state.show ? classes.show : ''}`}>
<form className={classes.form}>
<label htmlFor="aatitle-field">Title</label>
<input className={classes.input} type="text" id="aatitle-field" name="title" value="" />
<button className={`btn ${classes.button}`}>Save</button>
</form>
</div>
)
}
}
export default injectSheet(styles)(Modal)
First of all, please use the classnames library to generate classNames, it's so more elegant.
Secondly, classes are applied in order they are parsed by the brower. So what you're doing is tricky. How can we know if the modal class is parsed before show? (This is a requirement in your current code). You can simply move the modal declaration before the show declaration in styles and surely this will work, but this is a fragile solution. Better is to use a show and hide class and apply them according the state. This removes the dependency of which style class is loaded first. So remove display:none from modal and introduce hide:
Something like:
import React, { Component } from 'react'
import injectSheet from 'react-jss'
const styles = {
show: {
display: 'block',
},
hide: {
display: 'none',
},
modal: {
background: 'rgba(0, 0, 0, 0.3)',
position: 'fixed',
top: 0,
left: 0,
bottom: 0,
right: 0,
width: '100%',
height: '100%',
},
form: {
maxWidth: '440px',
margin: '15px',
padding: '30px',
background: '#fff',
position: 'fixed',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)'
},
input: {
width: '100%',
marginBottom: '15px'
},
button: {
width: '100%'
}
}
class Modal extends Component {
constructor(props) {
super(props)
this.state = {
show: false
}
this.toggleModal = this.toggleModal.bind(this)
}
toggleModal() {
this.setState({show: !this.state.show})
}
render() {
const { classes } = this.props
return (
<div className={`${classes.modal} ${this.state.show ? classes.show : classes.hide}`}>
<form className={classes.form}>
<label htmlFor="aatitle-field">Title</label>
<input className={classes.input} type="text" id="aatitle-field" name="title" value="" />
<button className={`btn ${classes.button}`}>Save</button>
</form>
</div>
)
}
}
export default injectSheet(styles)(Modal)
```

Resources