Material-Table: Change a specific action's colour on click / state change - reactjs

How can I style a particular MT action based on a different state?
Currently I'm doing something like this:
actions={[
rowData => {
const active = rowData && selected && rowData.tableData.id === selected.tableData.id;
return {
icon: 'bug_report',
iconProps: { color: active ? 'secondary' : 'primary' },
onClick: (event, info) => {
setSelected(info);
},
};
},
]}
However, instead of colouring the single selected element, it just stays as primary and does nothing. Why? Is it because the actions is rendered for action row and the next row !== selected?

So based on what I understood, I followed your code and ended up with this:
actions={[
rowData => {
let active =
rowData &&
clickedRow &&
rowData.tableData.id === clickedRow.tableData.id;
return {
icon: "bug_report",
tooltip: "Report bug",
onClick: (event, rowData) => setClicked(rowData),
iconProps: { color: active ? "primary" : "secondary" }
};
}
]}
Here is the sandbox.
I hope this is what you were looking for, if not, I believe that the examples at the official docs on Selection feature may help you with this kind of behaviour.
good luck!

Related

Update nested React state?

I'm trying to update part of a state object that is nested. This is the object:
const [buttonObject, setButtonObject] = useState({
intro: [
{
id: '123',
name: 'first_intro_name',
selected: false,
},
{
id: '124',
name: 'second_intro_name',
selected: false,
},
],
experience: [
{
id: '789',
name: 'first_experience_name',
selected: false,
},
{
id: '8910',
name: 'second_experience_name',
selected: false,
},
],
});
When a button is clicked I want to toggle the selected state. I'm using a click handler that looks like this:
const handleButtonClick = ({ id, selected }) => {
if (id === '123') {
buttonsObject.intro.map(
pref => (pref.selected = pref.id === id ? !pref.selected : selected)
);
setButtonsObject(buttonsObject);
} else if (id === '124') {
buttonsObject.intro.map(
pref => (pref.selected = pref.id === id ? !pref.selected : selected)
);
setButtonsObject(buttonsObject);
}
};
It would handle experiences as well. The issue is that right now it seems like rather than updating the object it just overwrites the object or creates a new one. It also doesnt pass that information back down to the component even though I have it routed correctly so it should.
Is there better/correct syntax for updating nested state like this?
Thanks.
instead of checking again with if condition use higher order array function and spread operator to get optimal solution.
setButtonObject(previous => {
return {
...previous,
info: previous.info.map(item => item.id === id ? {
...item,
selected: true
} ? item)
}
})

AG Grid, Capture the event on click of chevron

Ag-grid provides onCellClicked event for when a cell is clicked. But I noticed that it doesn't trigger when we click the chevron. I need to capture this event for analytics as a user might click the chevron directly rather than the cell itself. Any idea how to achieve this or what event to use here? I am using detailRenderer to render child rows, if that helps. Here is one of the columnDefs item:
{
headerName: "Line Item",
field: "displayLineNumber",
width: "100px",
sortable: false,
expandable: true,
onCellClicked: (event) => {
event.node.setExpanded(!event.node.expanded);
if (event.node.expanded === true) {
DataLayerUtils.pushEvent(
"click",
{
name: "Open Line Item",
type: "button",
},
{
click: {
category: "Quote Detail Table Interactions",
},
}
);
} else {
DataLayerUtils.pushEvent(
"click",
{
name: "Collapse Line Item",
type: "button",
},
{
click: {
category: "Quote Detail Table Interactions",
},
}
);
}
},
rowClass: ({ node, data }) => {
return `cmp-product-lines-grid__row ${
!data?.children || data.children.length === 0
? "cmp-product-lines-grid__row--notExpandable"
: ""
}`;
},
detailRenderer: ({ data }) => (
<section className="cmp-product-lines-grid__row cmp-product-lines-grid__row--expanded">
<ProductLinesChildGrid
gridProps={gridProps}
license={gridProps.agGridLicenseKey}
columnDefiniton={
whiteLabelMode ? whiteLabelCols() : quoteDetailsCols()
}
data={data.children}
onModelUpdateFinished={() => {
markupChanged(mutableGridData);
}}
/>
</section>
),
},
There's two ways to achieve this:
Implement the Grid Event rowGroupOpened (see: https://www.ag-grid.com/react-data-grid/grid-events/#reference-rowGrouping-rowGroupOpened)
This will be triggered whenever the user opens a group node.
onRowGroupOpened={(params) =>
console.log('onRowGroupOpened', params.expanded)
}
Implement the Grid Event cellMouseDown (see: https://www.ag-grid.com/react-data-grid/grid-events/#reference-selection-cellMouseDown) to capture the chevron click event.
This will be triggered only when the user clicks the expand chevron
onCellMouseDown={(params) => {
const isMasterRowGroupExpanded = params.event.target.classList.contains(
'ag-icon-tree-closed'
);
if (isMasterRowGroupExpanded) {
console.log('onCellMouseDown', params.value);
}
}}
See both of these approaches implemented in the following plunkr.

Semantic UI dropdown not setting value after selecting option

I am using React semantic ui. I am rendering a dropdown in Fieldset. I have written code such that, once a option is selected, the options is updated such that the selected option is removed from the list. But when I select an option from the dropdown, the selected value is not displayed, rather it shows empty.
Here is my code:
This is my dropdown code:
<Dropdown
name={`rows.${index}.mainField`}
className={"dropdown fieldDropdown"}
widths={2}
placeholder="Field"
fluid
selection
options={mainFieldOptions}
value={row.mainField}
onChange={(e, { value }) => {
setFieldValue(`rows.${index}.mainField`, value)
updateDropDownOptions(value)
}
}
/>
My options:
let mainField = [
{ key: "org", text: "org", value: "org" },
{ key: "role", text: "role", value: "role" },
{ key: "emailId", text: "emailId", value: "emailId" },
]
Also, I have:
const [mainFieldOptions, setMainFieldOptions] = useState(mainField)
And,
const updateDropDownOptions = (value:any) => {
let updatedOptions: { key: string; text: string; value: string }[] = []
mainFieldOptions.forEach(option => {
if(option.key != value){
updatedOptions.push({ key:option.key , text:option.key, value:option.key })
}
})
setMainFieldOptions(updatedOptions)
console.log("mainfield", mainField)
}
In onChange, if I dont call updateDropDownOptions() method, the dropdown value is set. But when I call the method, its giving blank value. Please help.
There are few changes required in your code,
You are pushing the entire initialValues when you are adding a row which is an [{}] but you need to push only {} so change your code to initialValues[0] in your push method.
Its not needed to maintain a additional state for the options. You can filter the options based on the selected option in other rows which is available in the values.rows .
Util for filtering the options
const getMainFieldOptions = (rows, index) => {
const selectedOptions = rows.filter((row, rowIndex) => rowIndex !== index);
const filteredOptions = mainField.filter(mainFieldOption => !selectedOptions.find(selectedOption => mainFieldOption.value === selectedOption.mainField));
return filteredOptions;
}
Call this util when rendering each row
values.rows.length > 0 &&
values.rows.map((row, index) => {
const mainFieldOptions = getMainFieldOptions(values.rows, index);
Working Sandbox

Fluent UI dropdown selection

I am using fluent ui dropdown So, when i click on dropdown the first option is selected by default without selecting. If i click on dropdown and without selecting if i click somewhere on the page nothing should be selected and dropdown should be closed is what i want. Please help me with this.
Below is the code which iam using:
<Dropdown
disabled={this.state.HideLocationFilter}
selectedKey={selectedItem1 ? selectedItem1.key : undefined}
onChange={this._onChange1}
placeholder="Location"
options={[
{ key: 'UK', text: 'UK' },
{ key: 'Hyderabad', text: 'Hyderabad' },
{ key: 'Bangalore', text: 'Bangalore' },
{ key: 'Ahmedabad', text: 'Ahmedabad' },
{ key: 'Pune', text: 'Pune' },
{ key: 'Mumbai', text: 'Mumbai' },
{ key: 'USA', text: 'USA' }
]}
styles={dropdownStyles}
/>
private _onChange1 = (event: React.FormEvent<HTMLDivElement>, item: IDropdownOption): void => {
console.log(`Selection change: ${item.text} ${item.selected ? 'selected' : 'unselected'}`);
if(item.selected == true)
{
this.setState({ selectedItem1: item });
}
};
Initially it is like this:location dropdown
When i click on dropdown it is selecting by default(find picture of the same):dropdown when clicked
There is an issue currently that when you press on the default option and click outside of the browser window and then click back in the dropdown selects the first option.
The solution for this is to looked at the type of event and short circuit the onChange if the type === 'focus':
const onChange = useCallback((e, value) => {
if (e.type === 'focus') return
// do if not type 'focus'
}, [])
In my situation I needed also to trigger a re-render and augmented the above as such:
const onChange = useCallback((callback) => (e, value) => {
if (e.type === 'focus') return callback()
// do if not type 'focus'
}, [])
Then when calling it:
<Dropdown
{...props}
onChange={onChange(() => setOnChangeTriggered(true))}
/>

Single selection item that saves the state (REACT-NATIVE)

I currently have 4 TouchableHighlights and my code looks as follows:
state:
this.state = {
selected: null,
selectedButton: ''
}
Touchable Highlight (they're all the same except for text)
<TouchableHighlight
onPress={this.selectedButton}
underlayColor='blue
style={[styles.box, { backgroundColor: (this.state.selected === true ? 'red' : 'blue') }]}>
<Text>1</Text>
</TouchableHighlight>
my functions:
selectedButton = () => {
this._handlePress('flag', '1')
this.setState({
selected: true,
});
};
_handlePress(flag, button) {
if (flag == 1) {
this.setState({ selected: true });
}
this.setState({ SelectedButton: button })
}
Current behaviour: Whenever I select one button, all become highlighted and cannot be unpressed.
Desired behaviour: I want only one button to be selected at the time with its state being saved somewhere.
Note: I could get the desired behaviour by creating 4 different functions that contain a different flag number, however, I'd like to get this done in the cleanest way possible.
Any guidance please?
Thank you
You can create an array of button texts, then use .map(), which provides the current index value, to iterate through them. For example:
render() {
const renderHighlight = (text, index) => (
<TouchableHighlight
onPress={() => {
if(this.state.selectedIndex === index) {
// "Unpress" functionality
this.setState({selectedIndex: undefined});
}
else {
this.setState({selectedIndex: index
}
})
style={this.state.selectedIndex === index ?
{backgroundColor: "#0F0" : {}}
>
<Text>{text}</Text>
</TouchableHighlight>;
);
const buttons = ["button 0", "button 1", "button 2"];
return buttons.map((text, i) => this.renderHighlight(text, i));
}

Resources