How to make button in OverflowSet execute? - office-ui-fabric

How do I get a button in Office ui fabric's OverflowSet do something.
The example from https://developer.microsoft.com/en-us/fabric#/components/overflowset has the onClick event which never fires.
import * as React from 'react';
import { BaseComponent, css } from 'office-ui-fabric-react/lib/Utilities';
import { CommandBarButton } from 'office-ui-fabric-react/lib/Button';
import { SearchBox } from 'office-ui-fabric-react/lib/SearchBox';
import { IOverflowSetItemProps, OverflowSet } from 'office-ui-fabric-react/lib/OverflowSet';
import * as stylesImport from './OverflowSet.Example.scss';
const styles: any = stylesImport;
export class OverflowSetCustomExample extends BaseComponent<any, any> {
public render(): JSX.Element {
return (
<OverflowSet
items={[
{
key: 'upload',
name: 'Upload',
icon: 'Upload',
onClick: () => {
console.log("this never happens");
}
}
]}
overflowItems={[]}
onRenderOverflowButton={this._onRenderOverflowButton}
onRenderItem={this._onRenderItem}
/>
);
}
private _onRenderItem(item: IOverflowSetItemProps): JSX.Element {
if (item.onRender) {
return item.onRender(item);
}
return <CommandBarButton iconProps={{ iconName: item.icon }} menuProps={item.subMenuProps} text={item.name} />;
}
private _onRenderOverflowButton(overflowItems: any[] | undefined): JSX.Element {
return (
<CommandBarButton className={css(styles.overflowButton)} menuIconProps={{ iconName: 'More' }} menuProps={{ items: overflowItems! }} />
);
}
}
the click event never fires. How do I use the button?

Since you are providing an onRenderItem callback, you are taking over the component’s default render method. Therefore, you have to apply the onClick prop yourself directly:
private _onRenderItem(item: IOverflowSetItemProps): JSX.Element {
if (item.onRender) {
return item.onRender(item);
}
//
// this now returns the button with the onClick handler applied
//
return <CommandBarButton onClick={item.onClick} iconProps={{ iconName: item.icon }} menuProps={item.subMenuProps} text={item.name} />;
}

Related

How to call function in map loop (react native)?

This is my code. I am not sure what error exists.
When I click the image button, it calls proper function exactly.
If I click the first button, it calls toggleBooks() function correctly.
Then in that function, I want to use vidMute state value.
So I tried console.log('Video toggle', this.state.vidMute); then it gives me an error like the following image.
But if I print console.log('Video toggle'), then it works well.
How to use state value in that function?
export default class Video extends Component {
constructor(props) {
super(props)
this.state = {
vidMute: false,
audioShow: false,
callShow: false,
btn: [
{ func: this.toggleAudio, url: magic, de_url: de_magic },
{ func: this.endCall, url: endcall, de_url: de_endcall },
{ func: this.toggleBooks, url: camerarotate, de_url: de_camerarotate },
],
};
this.toggleAudio = this.toggleAudio.bind(this)
this.endCall = this.endCall.bind(this)
this.toggleBooks = this.toggleBooks.bind(this)
}
toggleBooks() {
console.log('Video toggle', this.state.vidMute);
}
endCall() {
console.log('Call toggle', this.state.audioShow);
}
toggleAudio() {
console.log('Audio toggle', this.state.callShow);
}
render() {
return (
<View>
{
this.state.btn.map((item, index) => (
<TouchableOpacity key={index} style={styles.iconStyle} activeOpacity={0.4} onPress={item.func}>
<Image source={this.state.lockState ? item.de_url : item.url} style={{ width: 70, height: 70 }} />
</TouchableOpacity>
))
}
</View>
)
}
}
this refers to the context of your function and not the context of your component. You can try to bind your method like this :
this.myMethod = this.myMethod.bind(this);
in your constructor.
Or use the fat arrow pattern (Highly recommanded) which automatically includes the binding to your component's context.
Here is a binding example on stackblitz
Here is the code :
import React, { Component } from 'react';
import { render } from 'react-dom';
import Hello from './Hello';
import './style.css';
class App extends Component {
constructor() {
super();
this.state = {
name: 'React',
items:[
{name:"item 1", func: () => this.test()},
{name:"item 2", func: () => this.test2()}
]
};
this.test = this.test.bind(this);
}
test() {
console.log('Hi', this.state.name);
}
test2() {
console.log('Hello', this.state.name); // Note this is not binded
}
render() {
return (
<div>
<Hello name={this.state.name} />
<p onClick={this.test}>
Start editing to see some magic happen :)
</p>
<div>
{
this.state.items.map(item => <div onClick={() => item.func()}>{item.name}</div>)
}
</div>
</div>
);
}
}
render(<App />, document.getElementById('root'));

How to uncheck all other checkbox if data is "No Preference"?

How do I uncheck all checkbox if the data is no preference? I don't know how to manipulate the data.
This is the index.js:
import React, { Component } from "react";
import { Text, View } from 'react-native';
import { CheckBox } from 'react-native-elements';
import { Colors } from '../../../themes/';
import style from "./style";
class CCheckBox extends React.Component {
/////////////////////////////
// constructor()
/////////////////////////////
constructor(props, context) {
super(props, context);
console.log('custom/ccheckbox/index.js constructor()');
this.state = {
checked: false,
};
}
/////////////////////////////
// handleCheck()
/////////////////////////////
handleCheck() {
this.setState({ selectedCheckbox }); // update selected item
}
render() {
return (
<CheckBox
iconType='material'
checkedIcon='check'
uncheckedIcon='check-box-outline-blank'
checkedColor={Colors.ORANGE}
checked={this.state.checked}
containerStyle={style.content}
onPress={() => this.handleCheck()}
/>
);
}
}
export default CCheckBox;
And this is my profalcoholpref.js:
import React, { Component } from "react";
import { ScrollView, View } from 'react-native';
import { Content } from 'native-base';
import CButton from '../cbutton/index';
import PopSelectList from './popselectlist';
import styleC from "../../common/style";
import style from "./style";
class PopAlcoholPref extends React.Component {
///////////////////////////////
// constructor()
///////////////////////////////
constructor(props, context) {
super(props, context);
console.log('custom/cfield/popalcoholpref.js constructor()');
this.state = {
selectedCheckbox: {},
visible: this.props.visible,
data: [
{
id : 1,
code : 'DON',
description : 'Do not drink',
},
{
id : 2,
code : 'INF',
description : 'Infrequently',
},
{
id : 3,
code : 'SOC',
description : 'Socially',
},
{
id : 4,
code : 'MOD',
description : 'Moderately',
},
{
id : 5,
code : 'ASN',
description : 'As Needed',
},
{
id : 5,
code : 'NOP',
description : 'No Preference',
},
]
};
}
///////////////////////////////
// componentWillReceiveProps()
///////////////////////////////
componentWillReceiveProps(nextProps) {
console.log('componentWillReceiveProps()');
this.setState({
visible: nextProps.visible
});
}
///////////////////////////////
// handleSave()
///////////////////////////////
handleSave() {
console.log('handleSave()');
this.setState({
visible: false
});
}
///////////////////////////////
// render()
///////////////////////////////
render() {
return (
<View>
<PopSelectList title='Alcohol Preference' data={this.state.data} visible={this.state.visible} handleSave={() => this.handleSave()} />
</View>
);
}
}
export default PopAlcoholPref;
How do I uncheck all other checkbox if no preference is checked? Is there any way I can manipulate the data? Index.js is the frontend and I manipulated the checkbox there and in the prefalcohol is where the data is being stored.
You will need a bit of a refactoring here I beleive.
You should move the state handling logic to the list. In the list you can manipulate all the checkboxes at the same time.
class List extends Component {
constructor(props) {
super(props);
this.state = {
checkBoxesList: [{
id: 1,
checked: false,
}, {
id: 2,
checked: false,
}]
}
}
unCheckAll() {
this.setState({ checkBoxesList: this.state.checkBoxesList.map(({ id }) => ({
id: id,
checked: false,
})) })
}
checkBoxSelected(id) {
const index = this.state.checkBoxesList.findIndex((value) => value.id === id);
this.setState({ checkBoxesList[index]: {
...this.state.checkBoxesList[index],
checked: !this.state.checkBoxesList[index].checked
})
}
renderCheckBoxes() {
return this.state.checkBoxesList.map(({id, checked}) => (
<CheckBox id={id} checked={checked} onPress={this.checkBoxSelected} />
))
}
render() {
return (
<View>
{this.renderCheckBoxes()}
</View>
)
}
}
So this Component handles the states for the checkboxes. You need to make sure that you also implement the callback inside the CheckBox component for the OnPress method. Now calling the UncheckAll method will uncheck all the checkboxes.
But also you have to put in some extra check before setting the checkBoxesList if the index does exist.

Confirm/warn dialog on back

Like in the web browser, we have onBeforeUnload (vs onUnload) to show a alert or some warning "there is unsaved data - are you sure you want to go back".
I am trying to do the same. I couldn't find anything in the docs of react-navigation.
I thought of doing something real hacky like this, but I don't know if its the right way:
import React, { Component } from 'react'
import { StackNavigator } from 'react-navigation'
export default function ConfirmBackStackNavigator(routes, options) {
const StackNav = StackNavigator(routes, options);
return class ConfirmBackStackNavigatorComponent extends Component {
static router = StackNav.router;
render() {
const { state, goBack } = this.props.navigation;
const nav = {
...this.props.navigation,
goBack: () => {
showConfirmDialog()
.then(didConfirm => didConfirm && goBack(state.key))
}
};
return ( <StackNav navigation = {nav} /> );
}
}
}
React navigation 5.7 has added support for it:
function EditText({ navigation }) {
const [text, setText] = React.useState('');
const hasUnsavedChanges = Boolean(text);
React.useEffect(
() =>
navigation.addListener('beforeRemove', (e) => {
if (!hasUnsavedChanges) {
// If we don't have unsaved changes, then we don't need to do anything
return;
}
// Prevent default behavior of leaving the screen
e.preventDefault();
// Prompt the user before leaving the screen
Alert.alert(
'Discard changes?',
'You have unsaved changes. Are you sure to discard them and leave the screen?',
[
{ text: "Don't leave", style: 'cancel', onPress: () => {} },
{
text: 'Discard',
style: 'destructive',
// If the user confirmed, then we dispatch the action we blocked earlier
// This will continue the action that had triggered the removal of the screen
onPress: () => navigation.dispatch(e.data.action),
},
]
);
}),
[navigation, hasUnsavedChanges]
);
return (
<TextInput
value={text}
placeholder="Type something…"
onChangeText={setText}
/>
);
}
Doc: https://reactnavigation.org/docs/preventing-going-back
On current screen set
this.props.navigation.setParams({
needUserConfirmation: true,
});
In your Stack
const defaultGetStateForAction = Stack.router.getStateForAction;
Stack.router.getStateForAction = (action, state) => {
if (state) {
const { routes, index } = state;
const route = get(routes, index);
const needUserConfirmation = get(route.params, 'needUserConfirmation');
if (
needUserConfirmation &&
['Navigation/BACK', 'Navigation/NAVIGATE'].includes(action.type)
) {
Alert.alert('', "there is unsaved data - are you sure you want to go back", [
{
text: 'Close',
onPress: () => {},
},
{
text: 'Confirm',
onPress: () => {
delete route.params.needUserConfirmation;
state.routes.splice(index, 1, route);
NavigationService.dispatch(action);
},
},
]);
// Returning null from getStateForAction means that the action
// has been handled/blocked, but there is not a new state
return null;
}
}
return defaultGetStateForAction(action, state);
};
Notes,
Navigating without the navigation prop
https://reactnavigation.org/docs/en/navigating-without-navigation-prop.html
NavigationService.js
function dispatch(...args) {
_navigator.dispatch(...args);
}
This can be accomplished by displaying a custom back button in the header, and capturing the hardware back-event before it bubbles up to the navigator.
We'll first configure our page to show a custom back button by overriding the navigation options:
import React, { Component } from 'react'
import { Button } from 'react-native'
function showConfirmDialog (onConfirmed) { /* ... */ }
class MyPage extends Component {
static navigationOptions ({ navigation }) {
const back = <Button title='Back' onPress={() => showConfirmDialog(() => navigation.goBack())} />
return { headerLeft: back }
}
// ...
}
The next step is to override the hardware back button. For that we'll use the package react-navigation-backhandler:
// ...
import { AndroidBackHandler } from 'react-navigation-backhandler'
class MyPage extends Component {
// ...
onHardwareBackButton = () => {
showConfirmDialog(() => this.props.navigation.goBack())
return true
}
render () {
return (
<AndroidBackHandler onBackPress={this.onHardwareBackButton}>
{/* ... */}
</AndroidBackHandler>
)
}
}

ReactNative and NativeBase Radio

I've tried to change the radio value in ReactNative App with NativeBase template. I want to get or set value from the radio after click it, exactly checked or not. But couldn't find a way to get or set value to it. Even the radio button never changed on the screen after click. The codes are like as below:
import React, { Component } from 'react';
import { TouchableOpacity, Image, View } from 'react-native';
import { connect } from 'react-redux';
import { actions } from 'react-native-navigation-redux-helpers';
import {
Container,
Header,
Title,
Content,
Text,
Button,
Icon,
InputGroup,
Input,
List,
ListItem,
Radio, } from 'native-base';
import { openDrawer } from '../../actions/drawer';
import { Col, Row, Grid } from 'react-native-easy-grid';
import styles from './styles';
import dimension from './global';
import Swiper from 'react-native-swiper';
const imgBoy = require('../../../images/icon_boy.png');
const imgGirl = require('../../../images/icon_girl.png');
const {
popRoute,
} = actions;
class SessionPage extends Component {
static propTypes = {
name: React.PropTypes.string,
index: React.PropTypes.number,
list: React.PropTypes.arrayOf(React.PropTypes.string),
openDrawer: React.PropTypes.func,
popRoute: React.PropTypes.func,
navigation: React.PropTypes.shape({
key: React.PropTypes.string,
}),
}
popRoute() {
this.props.popRoute(this.props.navigation.key);
}
constructor(props) {
super(props);
// console.log(this.props.navigation);
this.state = {
sliderCount : parseInt(this.props.navigation.behavior.length / 5) + 1,
sliderArray : [],
selected : false,
}
this.getSliderArray();
console.log(this.state);
}
getSliderArray() {
for (var i = 0; i < this.state.sliderCount; i++) {
var childArray = [];
for (var j = i * 5; j < 5 * (i + 1); j++) {
if (this.props.navigation.behavior[j] != null){
var unit = this.props.navigation.behavior[j];
unit.selected = true;
childArray.push(unit);
}
}
this.state.sliderArray.push({
index : i,
behaviors : childArray
})
}
}
selectRadio(i, j){
this.state.sliderArray[i].behaviors[j].selected = true;
}
render() {
const { props: { name, index, list } } = this;
return (
<Container style={styles.container}>
<Swiper style={styles.wrapper}
height={dimension.Height - 400}
width={dimension.Width - 40}
showsButtons={false}
showsPagination={true}>
{this.state.sliderArray.map((item, i) =>
<View style={styles.slide1} key={i}>
{item.behaviors.map((subitem, j) =>
<ListItem key={i + "-" + j} style={styles.cardradio}>
<Radio selected={this.state.sliderArray[i].behaviors[j].selected} onPress={() => this.selectRadio(i, j)} />
<Text>{subitem.behaviorName}</Text>
</ListItem>
)}
</View>
)}
</Swiper>
</Content>
</Container>
);
}
}
function bindAction(dispatch) {
return {
openDrawer: () => dispatch(openDrawer()),
popRoute: key => dispatch(popRoute(key)),
};
}
const mapStateToProps = state => ({
navigation: state.cardNavigation,
name: state.user.name,
index: state.list.selectedIndex,
list: state.list.list,
});
export default connect(mapStateToProps, bindAction)(SessionPage);
selectRadio(i, j){
this.state.sliderArray[i].behaviors[j].selected = true; <== This is the problem
}
When you call this.state = something after the component has mounted, it doesn't trigger update method of component life cycle. Hence view will not be updated.
You should be using this.setState() to update your views
this.setState({
slider = something
})
For more info, refer docs
this.setState() is an async method. After you make changes in getSliderArray(), it may not be reflected in immediate console.log
this.getSliderArray();
console.log(this.state);
You can pass callback to this.setState() to perform any action only after state is changed
this.setState({
// new values
}, function() {
// Will be called only after switching to new state
})

test with enzyme a react component with context: return an empty object

I'm trying to execute a dummy test with enzyme over a component. the test is about to check the context. even though I'm writing the same code as enzyme's documentation the context is always empty.
import React from 'react';
import { shallow } from 'enzyme';
import Overlay from '../../../../app/components/Overlay/Overlay';
describe('<Overlay />', () => {
it.only('return a context', () => {
const wrapper = shallow(<Overlay />, { context: { foo: 10 } });
console.log(wrapper.context());
// expect(wrapper.context().foo).to.equal(10);
});
})
the test's output is:
<Overlay />
{}
✓ return a context
where am I wrong?
Since the details of Overlay component is not given, I assume the context is not used in it (pls check childContextTypes and getChildContext are defined properly)
For example, refer the explanation for contexts in react documents
I have taken the same example to enable the test,
import React from 'react';
export default class Button extends React.Component {
render() {
return (
<button style={{ background: this.context.color }}>
{this.props.children}
</button>
);
}
}
Button.contextTypes = {
color: React.PropTypes.string,
};
class Message extends React.Component {
render() {
return (
<div>
{this.props.text} <Button>Delete</Button>
</div>
);
}
}
class MessageList extends React.Component {
getChildContext() {
return { color: 'purple' };
}
render() {
const children = this.props.messages.map((message) =>
<Message text={message.text} />
);
return <div>{children}</div>;
}
}
MessageList.childContextTypes = {
color: React.PropTypes.string,
};
I've created the test for Button component as below,
import React from 'react';
import { shallow } from 'enzyme';
import { expect } from 'chai';
import Button from '../../src/components/SampleComp';
describe.only('<Button />', () => {
it('assert for context', () => {
const wrapper = shallow(
<Button />,
{ context: { color: 'red' } }
);
expect(wrapper.context().color).to.equal('red');
expect(wrapper.context('color')).to.equal('red');
});
});
<Button />
✓ assert for context
1 passing (214ms)
This will assert it correctly.