I am printing to the console a user selection from a Fluent UI dropdown. The only challenge I'm running into is grabbing the actual selection. I don't see how FLUENT is handling that, as it's not mentioned in their documentation.
My code looks like this:
<Dropdown
placeholder="Select Reason"
label="Send to History with Reason"
onChange={(selectedKey) => this.onReasonChange(selectedKey)}
options={this.graveyardReasons}
onRenderLabel={this.onRenderLabel}
styles={{
root: {
maxWidth: 300,
minWidth: 300,
},
}}
/>
And here's the function that gets called on a selection being made:
onReasonChange = (selection) => {
console.log('selection.target: ', selection.target);
};
This is what prints to the console:
<div data-is-focusable="true" id="Dropdown23" tabindex="0" role="listbox" aria-haspopup="listbox" aria-expanded="false" aria-labelledby="Dropdown23-label Dropdown23-option" class="ms-Dropdown dropdown-155"><span id="Dropdown23-option" class="ms-Dropdown-title title-220" aria-live="polite" aria-atomic="true" aria-invalid="false" role="option" aria-setsize="4" aria-posinset="2" aria-selected="true">Selection 1</span></div>
How do I pull the value Selection 1 from this div? And, is there a simpler way to get the value that I'm simply missing?
As Fluent UI docs says about onChange:
(
event: React.FormEvent<HTMLDivElement>,
option?: IDropdownOption,
index?: number
) => void
Callback issued when the selected option changes.
So selected value is the second parameter. Let me show an example:
const DropdownBasicExample: React.FunctionComponent = () => {
const onReasonChange = (e, selection) => {
console.log('selection: ', selection);
};
return (
<Stack tokens={stackTokens}>
<Dropdown
placeholder="Select an option 1"
label="Basic uncontrolled example"
onChange={onReasonChange}
options={options}
styles={dropdownStyles}
/>
</Stack>
);
};
Related
i have a textarea in react that i want to create and i want the cursor the start at the beginning when it loses focus. How do i solve this issue?
codesandbox: https://codesandbox.io/s/affectionate-tu-2cd6j6?file=/src/App.js
``
export default function App() {
const textareaRef = useRef();
return (
<div>
<textarea
ref={textareaRef}
id="my-text-area"
defaultValue={"Hello World!"}
rows="2"
tabIndex="-1"
style={{ resize: "none", overflow: "hidden" }}
onBlur={() => textareaRef.current.setSelectionRange(0, 0)}
></textarea>
</div>
);
}
tried using onblur function with setSelectionRange but its not working for some reason.
Played a bit with your code, seems like you are missing focus() call after changing the selection range.
onBlur={() => {
textareaRef.current.setSelectionRange(0, 0);
textareaRef.current.focus();
}}
Then, the cursor will appear at the beginning of the textarea content.
https://codesandbox.io/s/suspicious-tdd-r8t77j?file=/src/App.js
I have 2 select boxes that i am building uisng the autocomplete component in material ui - https://mui.com/material-ui/react-autocomplete/#checkboxes
I have a edge case im trying to fix:
A user selects both "Consumer Electronics" and "Fashion & Apparel" in the first main industries select box.
A user then selects in the second sub industries select box, "Computer & Laptops" and "Eyewear". (1 from each group).
If a user then removes "Fashion & Apparel" from the first select box, all of the items that were selected with that category should be removed in the second select box.
example:
https://codesandbox.io/s/checkboxestags-demo-material-ui-forked-qox19g?file=/demo.tsx
You can see in the second "Autocomplete" im using the "filterOptions" to only show options depending on the first "Autocomplete"
also see data.json in the example for the raw data.
code:
return (
<Autocomplete
multiple
options={subIndustries}
filterOptions={(x) =>
x.filter((x) =>
getValues("main_industries").find((m) => m.name === x.category)
)
}
groupBy={(option) => option.category}
disableCloseOnSelect
getOptionLabel={(option) => option.name}
onChange={(_, newValue: any) => {
setValue("sub_industries", newValue, { shouldValidate: true });
}}
defaultValue={getValues("sub_industries")}
onInputChange={(_, val) => {
console.log(val);
}}
renderOption={(props, option, { selected }) => (
<li {...props}>
<Checkbox
icon={uncheckedIcon}
checkedIcon={checkedIcon}
style={{ marginRight: 8 }}
checked={selected}
/>
{option.name}
</li>
)}
renderInput={(params) => (
<TextField
{...params}
label="Sub Industries"
placeholder="Sub Industries"
/>
)}
/>
);
};
Before all, some points related to your code:
On Autcocomplete of SubIndustrySelector function, you should use value instead defaultValue. defaultValue should be use when component is not controlled.
You also need to include the isOptionEqualToValue prop to both Autocomplete . Your code claims to use it. According to Mui docs, isOptionEqualToValue is necessary for:
Used to determine if the option represents the given value. Uses strict equality by default. ⚠️ Both arguments need to be handled, an option can only match with one value.
So you just need to guarantee that the option is equal the value with:
isOptionEqualToValue={(option, value) => option.id === value.id}
Related to the issue you are facing, the SubIndustrySelector isn't get update because you are controlling what tags should render using getValues instead watch from react-hook-form.
According to react-hook-form docs, getValues are:
An optimized helper for reading form values. The difference between watch and getValues is that getValues will not trigger re-renders or subscribe to input changes
(You can check more about it here and here.)
So, your SubIndustrySelector function should be something like this:
// WATCH ON sub_industries
const watchSubIndustries = watch("sub_industries");
const SubIndustrySelector = () => {
if (!dataJson) {
return <Skeleton height={"100%"} variant="text" />;
}
const subIndustries = dataJson
.map((i) => {
return i.industries;
})
.flat()
.filter(Boolean);
return (
<Autocomplete
multiple
options={subIndustries}
filterOptions={(x) =>
x.filter((x) =>
getValues("main_industries").find((m) => m.name === x.category)
)
}
groupBy={(option) => option.category}
disableCloseOnSelect
getOptionLabel={(option) => option.name}
// isOptionEqualToValue CONTROL
isOptionEqualToValue={(option, value) => option.id === value.id}
onChange={(_, newValue: any) => {
setValue("sub_industries", newValue, { shouldValidate: true });
}}
//defaultValue={watchSubIndustries}
value={watchSubIndustries}
renderOption={(props, option, { selected }) => {
return (
<li {...props}>
<Checkbox
icon={uncheckedIcon}
checkedIcon={checkedIcon}
style={{ marginRight: 8 }}
checked={selected}
/>
{option.name}
</li>
);
}}
renderInput={(params) => {
return (
<TextField
{...params}
label="Sub Industries"
placeholder="Sub Industries"
/>
);
}}
/>
);
};
You can check the code above working in this code sample.
As you can see, the changes that i did are:
before the SubIndustrySelector function i added a watch to sub_industries -> watchSubIndustries.
Added isOptionEqualToValue prop
Changed defaultValue to value prop and add watchSubIndustries as value.
If you have any questions just ask in the comments.
I am using select in the 'antd' (Ant Design) library. The selected option is showing the value of the option instead of the the label when using an onChange Handler.
I am adding the working example sandbox. In the example I have commented out the onChange in DATA1 so it works as expected.
<Select
// onChange={(key, val) => onChangeHandler1(key, val)}
placeholder="Select any one"
style={{ width: 120 }}
options={data
.filter((d) => d.disabled === false)
.map((_) => {
return {
label: _.name,
value: _.id
};
})}
bordered={false}
/>
When the onChange is commented out the behaviour is as expected, the selected value shows the label(name) instead of the value(id).
Try this: Just add the value prop to both selects. Eg:-
<Select value={option1} .../>
<Select value={option2} .../>
I am looking for something like this to implement in ReactJS/Material-UI. Any existing component or library for this?
You can do that with the Autocomplete component by overriding the ListboxProps of the Listbox which is the container of the dropdown and set the CSS grid to display 3 equal width columns using the fr unit:
<Autocomplete
ListboxProps={{
style: {
display: "grid",
gridTemplateColumns: "1fr 1fr 1fr",
maxHeight: "initial",
}
}}
Now assuming the option has the following type:
type TagOption = {
tag: string;
views: number;
description: string;
};
In the Stackoverflow dropdown, there are only 6 options at max, so we need to restrict the number of options shown in the Listbox:
import Autocomplete, { createFilterOptions } from "#mui/material/Autocomplete";
const _filterOptions = createFilterOptions<TagOption>();
const filterOption = (props, state) => {
const results = _filterOptions(props, state);
return results.slice(0, 6);
};
<Autocomplete filterOptions={filterOption}
We also want to create a customized option component for each grid item. Here is a minimal example without any much styles to get you started:
function OptionItem({ option, ...other }) {
return (
<li
{...other}
style={{
display: "block"
}}
>
<div>
<Chip label={option.tag} />
{option.views}
</div>
<div>{option.description}</div>
</li>
);
}
renderOption={(props, option, { selected }) => (
<OptionItem {...props} option={option} />
)}
And finally set the isOptionEqualToValue and getOptionLabel to ensure that the option get filtered properly and the input display the correct tag when selected:
isOptionEqualToValue={(option, value) => option.tag === value.tag}
getOptionLabel={(option) => option.tag}
Live Demo (Source)
That's not a perfect fit, but you can use autocomplete (using multiple, customizable and asynchronous)
Research on propagation has not given me answers. I have a React list using divs. Each item has a clickable row that takes the user to another page. But within the row, there are dropdown elements for updating the item. stopPropagation has been added to the dropdowns so that the user does not go to another page.
However, if you open the dropdown and then click off to the side because you changed your mind, the row action gets triggered anyway and takes the user to another page. How do I stop that from happening? I cannot target only the row container for redirecting to another page because I can't seem to add a reference to that element that I can match on in event.target. Is there another way to achieve this?
DROPDOWN
const select = () => (
<Select value={value} onChange={(e) => onChange(e)}>
{items.map((option) => (
<MenuItem className={menuItemClass} key={option.id} value={option.id}>
{option.name}
</MenuItem>
))}
</Select>
)
ONCHANGE
const onChange = async (event) => {
event.stopPropagation();
updateItem();
...
ROW
<TableRowBody onClick={(event) => onClickRow(rowItem, event)}>
...
<select />
...
</TableRowBody>
ONCLICKROW
const onClickRow = (value, event) => {
setValue(value);
};
If the clickable element can be a sibling of the select, you can do a css hack like this:
select:focus ~ .clickable-div{
pointer-events:none
}
.clickable-div{
position: absolute;
height: 100%;
width: 100%;
}
When the select is open (focus), it's sibling, .clickable-div will not respond to clicks.
<TableRowBody >
....
<div onClick={(event) => onClickRow(rowItem, event)} className="clickable-div"></div>
<select />
...
</TableRowBody>