React JS mapped array - render a single item - reactjs

I have a React front end, which renders an array of rows based on objects from an API. I am mapping the object array, which works as intended. If the condition is met, an icon button is displayed. If the condition of the child elements below (sector.properties.length === 0) is true multiple times, multiple buttons will be rendered in the row. I am trying to only display a single button if the condition is true, but am struggling to figure it out.
{data.sectors.map((sector, index) => (
<SingleLineCell
key={`${data.productName}Product${index}`}
>
{sector.properties.length === 0 && (
<button
type="button"
onClick={() =>
showModal('DeleteData', {
form: data,
onConfirmDelete: () => {
onConfirmDelete(data);
}
})
}
>
<IconDelete responsive />
<span className="sr-only">Delete {data.productName} Product</span>
</button>
)}
</SingleLineCell>
))}
So this is what is currently rendered. I want to only render the first button, even when the condition is true multiple times:

If the map is just used to display this button then instead of map use first some to check if the data satisfies the condition and then just print the button.
{data.sectors.some((sector) => sector.properties.length === 0) && (
<SingleLineCell
key={`${data.productName}Product`}
>
<button
type="button"
onClick={() =>
showModal('DeleteData', {
form: data,
onConfirmDelete: () => {
onConfirmDelete(data);
}
})
}
>
<IconDelete responsive />
<span className="sr-only">Delete {data.productName} Product</span>
</button>
</SingleLineCell>
)}

Related

Conditioning a specific item from a mapped dynamic array in React JS

I want to have an edit mode to each field in a div that is mapped out from an array that I fetch from firbase. I succeeded doing that by conditioning the rendered field to the value of a boolean (editField) which I then manipulate using useState, like so:
in the functions seen up there I can manipulate the value of editTitle, so as to switch between the two functions by double clicking or clicking a button, and also update the field value in Firebase. as such:
this all works fine. HOWEVER,
if there are more that one divs rendered from the tasks[], then thay are obviously all effected to the flipping of editTitle's value from false to true, and by double clicking one field, all fields of same name in all divs swithc to edit mode. as such:
what can I do to target only the field in the task I want to edit? I've tried using the elemnt.id and index in some way bat can't seem to come up with the correct method...
const ifEditTitleIsTrue = (element, index) => {
return (
<div>
<input
type="text"
defaultValue={element.Title}
onChange={(e) => setUpdatedTitle(e.target.value)}
/>
<button className="exit__editmode-btn btn" onClick={exitEditMode}>
X
</button>
<button
className="update__edit-btn btn"
id="updateTitle"
onClick={(e) => updateField(e, element.id)}
>
ok
</button>
</div>
);
};
// if editTitle = false (default):
const ifEditTitleIsFalse = (element, index) => {
return (
<h3
id={index}
className="task-title"
onDoubleClick={() => setEditTitle(true)}
>
{element.Title}
</h3>
);
};
// edit mode for inCharge field
const ifEditInChargeIsTrue = (element, index) => {
return (
<div>
{
<GetCollaboratorsForEditMode
catchValueInCharge={catchValueInCharge}
/>
}
<button className="exit__editmode-btn btn" onClick={exitEditMode}>
X
</button>
<button
className="update__edit-btn btn"
id="updateInCharge"
onClick={(e) => updateField(e, element.id)}
>
ok
</button>
</div>
);
};
{tasks[0] &&
tasks.map((element, index) => (
<div id={element.id} className="task" key={element.id}>
{editTitle
? ifEditTitleIsTrue(element, index)
: ifEditTitleIsFalse(element, index)}
You need to keep track of what element is in edit mode. You can do it by storing the element id in your editTitle state, instead of just a boolean
const ifEditTitleIsFalse = (element, index) => {
...
onDoubleClick={() => setEditTitle(element.id)}
...
};
The condition to render an element in edit mode or view mode would change to:
{editTitle === element.id
? ifEditTitleIsTrue(element, index)
: ifEditTitleIsFalse(element, index)}
I've solved it!!!
insted of EditTitle being a boolean, it's just an empty string.
then the condition is editTitle === index ? some function : some othe function;
and the doubleclick is (()=> setEditTitle(index)).

React Mui Autocomplete resets scroll after selecting values

So I'm trying to set up a mui-autocomplete component with additional buttons (Clear all (clear all values and close dropdown) + Apply (set value and close dropdown)) using ListboxComponent.
Issues:
when selecting options from the bottom of the list, the scroll position is reset to the top
cannot close the dropdown programmatically
Here is the ListboxComponent
ListboxComponent={(listBoxProps) => {
return (
<div>
<ul {...listBoxProps} />
<div>
<button
onMouseDown={(event) => {
// Disable blur
event.preventDefault();
}}
onClick={() => {
// clear values
setSelected([]);
}}
>
Clear All
</button>
<button
onMouseDown={(event) => {
// Disable blur
event.preventDefault();
}}
onClick={() => {
// apply value
}}
>
Apply
</button>
</div>
</div>
);
The options are rendered as follows:
renderOption={(optionProps, option, optionState) => {
return (
<li {...optionProps}>
<Checkbox
icon={icon}
checkedIcon={checkedIcon}
checked={optionState.selected}
/>
{option}
</li>
);
}}
So I'm using state to keep track of saving the selected values:
const [selectedResult, setSelected] = useState([]);
And when the option is selected - the state is updated
onChange={(event, selectedOptions) => {
setSelected(selectedOptions);
}}
But when the state changes, the component is re-rendered and the scroll is reset. It also seems that I can't use local variables to store the intermediate result, as the state won't update and the checkbox won't update.
StackBlitz link
Is there anything I can do to achieve this?

React toggling between clickable words that pull up <Elements/>

Somewhat new to React.
I want to be able to toggle between React elements CreateUser, EditUser, and ListUser.
It would be great to have a clickable text that when selected pulls up that element and its
corresponding stuff. In the case of Create its a field with a save button.
What is the best way to have my CrudSelector class list all three texts, make them clickable and pull up the stuff inside toggling to the selected one at a time?
Welcome. Always a good idea to share code with your question.
You'll want to use some kind of state which tells your parent component which child component to show. Kind of like this in your React functional component:
const [ featureToShow, setFeatureToShow ] = useState('list')
return (
<>
{/* Show some buttons to change the state */}
<button onClick={() => setFeatureToShow('list')}>Show User List</button>
<button onClick={() => setFeatureToShow('create')}>Create User</button>
<button onClick={() => setFeatureToShow('edit')}>Edit User</button>
{/* Depending on what is in the state show that component */}
{ featureToShow === 'list' ? <ListUser /> : null }
{ featureToShow === 'create' ? <CreateUser /> : null }
{ featureToShow === 'edit' ? <EditUser /> : null }
</>
)
In reality you'll probably want to enable clicking on the user's name in ListUser to show the EditUser component.

Adding a specific styling to a specific item of a mapped array in React

const allTodos = [{id: 1, name: 'whatever user type'}, { }, { }, .... { }] // Defined as an array using setState in other file. Imported in this file as allTodos using props.
export const Todos = (props) => {
props.allTodos.map((prev) => {
return (
<div id="item_container">
<button type='button' className = 'check_button'
onClick = {() => setActiveTodos(prev.id)}>
<img src = {check} alt="" id = "check_img"></img>
</button>
<li id="li_item">{prev.name}</li>
</div>
)}
}
Now, the question is I want to add some specific style to the element(Button) clicked upon on. The styling I want to add is just change the background of the Button clicked upon.
I tried using conditional to set className but the styling was added to every item. So, it didn't work out.
conditional class - selecting particular class based on condition (in this case - if the activeTodos state == current index)
props.allTodos.map((prev, i) => {
<button type = 'button' key ={i}
className= {`${prev.id == activeTodos ? "active" : "inactive"}
onClick={e => setActiveTodos(prev.id)}}
</button>
}
There is some combinations (eg. There can be selected only 1 item per time or 4)
If You wan't 4 items selected (eg from 6) - then there is possiblity to hold active ids in array.

how to test a component if the component returns empty array in the beginning with react-testing-library?

i am trying to learn react-testing-library and i got stuck at this point. I have one array that returns an empty array in the start but then i can add items inside the list. Before i add an item, component shows no data available after i add the items it shows some text and delete button next to each item. The test will be about clicking to delete button and opening a modal with this click. My test finds the delete button but it throws error saying "Expected: 1 Received: [ ]" because it always returns "no data available" in the beginning when the component renders. Clearly its because the array returns empty in the beginning but i don't get it. here i share my component.
<div>
{props.formData && props.formData.length !== 0 ? (
props.formData.map((widget, index) => (
<div
id="component-item"
className="widget-wrapper"
key={index}
>
<div className="name-language">
<p>Name: {widget.name}</p>
<p>Language: {widget.language}</p>
</div>
<button
onClick={() => toggleVisible(index)}
type="button"
className="delete-button"
data-testid="component-delete"
>
Delete
</button>
{selectedItem == index ? (
modalVisible == true ? (
<ModalForm
id="component-modal"
toggleVisible={toggleVisible}
onClick={() => handleDeleteWidget(index)}
/>
) : null
) : null}
</div>
))
) : (
<div>
<p className="no-data">
No data available
</p>
</div>
)}
</div>
in the below you can see the test i wrote for it
test("The deleting widget asks confirmation with a modal window.", () => {
render(<WidgetsList />);
const deleteWidget = screen.queryAllByText("Delete");
const modalComponent = document.querySelector("#component-modal");
expect(deleteWidget).toBe(1);
fireEvent.click(deleteWidget);
expect(modalComponent).toBe(1);
});
i would very much appreciate if you show what i'm missing.
Not sure on what modalVisible is. Is it a state or a prop? You will need to mock that is some way to make the modal visible. Anyway, I would create at least two tests, one with formData empty, and other with some data. You will need to pass some mocked data to the component props, something like:
describe('WidgetsList', () => {
test('no data available', () => {
render(<WidgetsList formData={[]} />);
expect(/No data available/).toBeInTheDocument();
});
test('data available', () => {
const mockedFormDataProp = [{ name: 'whatever', language: 'whatever' }];
render(<WidgetsList formData={mockedFormDataProp} />);
expect(screen.getByText(/No data available/)).not.toBeInTheDocument();
fireEvent.click(screen.getByText(/Delete/));
// attach a data-testid prop to your modal, in case there is some text
// add test it like screen.getByText(...)
expect(screen.getByTestId('the-modal')).toBeTruthy();
});
});

Resources