import * as React from 'react';
import { IContextualMenuProps, IIconProps, Stack, IStackStyles } from '#fluentui/react';
import { CommandBarButton } from '#fluentui/react/lib/Button';
export interface IButtonExampleProps {
// These are set based on the toggles shown above the examples (not needed in real code)
disabled?: boolean;
checked?: boolean;
}
const menuProps: IContextualMenuProps = {
items: [
{
key: 'emailMessage',
text: 'Email message',
iconProps: { iconName: 'Mail' },
},
{
key: 'calendarEvent',
text: 'Calendar event',
iconProps: { iconName: 'Calendar' },
},
],
};
const addIcon: IIconProps = { iconName: 'Add' };
const mailIcon: IIconProps = { iconName: 'Mail' };
const stackStyles: Partial<IStackStyles> = { root: { height: 44 } };
export const ButtonCommandBarExample: React.FunctionComponent<IButtonExampleProps> = props => {
const { disabled, checked } = props;
// Here we use a Stack to simulate a command bar.
// The real CommandBar control also uses CommandBarButtons internally.
return (
<Stack horizontal styles={stackStyles}>
<CommandBarButton
iconProps={addIcon}
text="New item"
// Set split=true to render a SplitButton instead of a regular button with a menu
// split={true}
menuProps={menuProps}
disabled={disabled}
checked={checked}
/>
<CommandBarButton iconProps={mailIcon} text="Send mail" disabled={disabled} checked={checked} />
</Stack>
)
};
How to disable this icon
in fluent-ui button component
which props or method
i need to add to disable it.
can anyone help regarding it
as i am not able to
get into fluent ui react
https://developer.microsoft.com/en-us/fluentui#/controls/web/button
You can specific the menuIconProps (read here) which will not display the icon if specified as empty string.
Add this property
menuIconProps={{iconName: ""}}
<CommandBarButton
iconProps={addIcon}
text="New item"
// Set split=true to render a SplitButton instead of a regular button with a menu
// split={true}
menuIconProps={{iconName: ""}}
menuProps={menuProps}
disabled={disabled}
checked={checked}
/>
With above change, the button will be displayed as shown below:
Related
I am using react-select to implement a multi-value drop down but using our internal UI component library to render the selected values in the input box. I am overriding the MultiValueContiner with our component. It renders fine, I can select items and they are added and rendered in the input box. The problem is with removing items. What can I access from the onClick handler of the component to remove it from the currently selected options? Do I simply need to add the currentValue & setCurrentValue state accessors to each menu option items and access through e.g. props.data.setCurrentValue()?
Custom MultiValueContainer
import { useState } from 'react';
import Select, { InputActionMeta, components, MultiValueGenericProps, MultiValue, ActionMeta } from 'react-select';
// import component from internal UI lib, dummy import here
import MyUIObject from './MyUIObject';
interface MenuOption {
value: string;
label: string;
}
export interface Props {
title: string;
items: MenuOption[];
}
const MyUIObjectValueContainer = (props: MultiValueGenericProps<MenuOption>) => {
return (
<components.MultiValueContainer {...props}>
<MyUIObject
text={props.data.label}
onClick={ (e) => {
e.stopPropagation();
e.preventDefault();
// HOW TO REMOVE FROM SELECTED OPTIONS ???
}}
/>
</components.MultiValueContainer>
);
};
function MyCustomMultiSelect(props: Props) {
const [inputValue, setInputValue] = useState('');
const [currentValue, setCurrentValue] = useState<MenuOption[]>([]);
function handleInputChange(newValue: string, actionMeta: InputActionMeta) {
if (actionMeta.action === 'input-change') {
setInputValue(newValue);
}
}
// clear manually typed search string from input
function handleOnBlur() {
setInputValue('');
}
function handleOnChange(newValue: MultiValue<MenuOption>, actionMeta: ActionMeta<MenuOption>) {
setCurrentValue( newValue as MenuOption[] );
}
return (
<Select
isMulti
isClearable
isSearchable
options={props.items}
closeMenuOnSelect={false}
onInputChange={handleInputChange}
inputValue={inputValue}
onBlur={handleOnBlur}
components={{ MultiValueContainer: MyUiObjectValueContainer }}
value={currentValue}
onChange={handleOnChange}
/>
);
}
export default MyCustomMultiSelect;
You haven't shared the code for your custom option component, so I'm assuming you built it correctly and made sure react-select's props are being spread into the custom component react-select docs.
In the case of multi select, the state you manage should be an array of selected options containing a label and an id properties. When you click on a selected option to remove it, react-select returns a new array of selected values with the option you clicked on filtered out. You should be able to just grab that returned array and set your selected option state I'm demoing in this simplified code:
import { useState } from "react";
import Select from "react-select";
export const options = [
{ label: "Option 1", value: 1 },
{ label: "Option 2", value: 2 },
{ label: "Option 3", value: 3 },
{ label: "Option 4", value: 4 },
{ label: "Option 5", value: 5 }
];
const App = () => {
const [value, setValue] = useState([]);
const handleChange = (e) => setValue(e);
return (
<Select
value={value}
options={options}
isMulti
onChange={handleChange}
closeMenuOnSelect={false}
/>
);
};
export default App;
You can have a look in this sandbox as well to see it working
I'm using react-wysiwg and i'm trying to change the color of toolbar icons when ever user hovers on it or when the user selects it.
As the toolbar bold / italic icon is in base64 encoded format i'm not able to change the color of the icon whenever user hovers on it - Is there a way to change it? - filter: "invert(0.8)" works but is there way to change the color?
Also, is there a way to change the color of the icon when the selected styling is in active state?
const useStyles = makeStyles((theme) => {
return {
icons: {
"& img:hover": {
color: "red", //This doesn't work
filter: "invert(0.8)", //This works
},
},
};
});
const EditorComponent = () => {
const [editorState, setEditorState] = useState(EditorState.createEmpty());
const classes = useStyles();
return (
<Editor
wrapperClassName={classes.wrapper}
editorClassName={classes.editorClass}
editorState={editorState}
onEditorStateChange={setEditorState}
toolbar={{
options: ["inline"],
inline: {
options: ["bold", "italic"],
className: classes.inline,
bold: {
icon: undefined,
className: classes.icons,
},
italic: {
icon: undefined,
className: classes.icons,
},
},
// className: classes.toolbar
}}
/>
);
};
I have a material ui stepper in which there are multiple forms using react-hook-form. When I change between steps, I would expect a new useForm hook to create new register functions that would register new form fields, but when I switch between steps, the second form has the data from the first. I've seen at least one question where someone was trying to create two forms on the same page within the same component but in this case I am trying to create two unique forms within different steps using different instances of a component. It seems like react-hook-form is somehow not updating the useForm hook or is recycling the form fields added to the first register call.
Why isn't react-hook-form using a new register function to register form fields to a new useForm hook? Or at least, why isn't a new useForm hook being created between steps?
DynamicForm component. There are two of these components (one for each step in the stepper).
import { Button, Grid } from "#material-ui/core";
import React from "react";
import { useForm } from "react-hook-form";
import { buttonStyles } from "../../../styles/buttonStyles";
import AppCard from "../../shared/AppCard";
import { componentsMap } from "../../shared/form";
export const DynamicForm = (props) => {
const buttonClasses = buttonStyles();
const { defaultValues = {} } = props;
const { handleSubmit, register } = useForm({ defaultValues });
const onSubmit = (userData) => {
props.handleSubmit(userData);
};
return (
<form
id={props.formName}
name={props.formName}
onSubmit={handleSubmit((data) => onSubmit(data))}
>
<AppCard
headline={props.headline}
actionButton={
props.actionButtonText && (
<Button className={buttonClasses.outlined} type="submit">
{props.actionButtonText}
</Button>
)
}
>
<Grid container spacing={2}>
{props.formFields.map((config) => {
const FormComponent = componentsMap.get(config.component);
return (
<Grid key={`form-field-${config.config.name}`} item xs={12}>
<FormComponent {...config.config} register={register} />
</Grid>
);
})}
</Grid>
</AppCard>
</form>
);
};
N.B. The images are the same because the forms will contain the same information, i.e. the same form fields by name.
Entry for first form:
Entry for the second form:
Each form is created with a config like this:
{
component: DynamicForm,
label: "Stepper Label",
config: {
headline: "Form 1",
actionButtonText: "Next",
formName: 'form-name',
defaultValues: defaultConfigObject,
formFields: [
{
component: "AppTextInput",
config: {
label: "Field 1",
name: "field_1",
},
},
{
component: "AppTextInput",
config: {
label: "Field2",
name: "field_2",
},
},
{
component: "AppTextInput",
config: {
label: "Field3",
name: "field_3",
},
},
{
component: "AppTextInput",
config: {
label: "Field4",
name: "field4",
},
},
],
handleSubmit: (formData) => console.log(formData),
},
},
And the active component in the steps is handled like:
import { Button, createStyles, makeStyles, Theme } from "#material-ui/core";
import React, { useContext, useEffect, useState } from "react";
import { StepperContext } from "./StepperProvider";
const useStyles = makeStyles((theme: Theme) =>
createStyles({
buttonsContainer: {
margin: theme.spacing(2),
},
buttons: {
display: "flex",
justifyContent: "space-between",
},
})
);
export const StepPanel = (props) => {
const { activeStep, steps, handleBack, handleNext, isFinalStep } = useContext(
StepperContext
);
const [activeComponent, setActiveComponent] = useState(steps[activeStep]);
const classes = useStyles();
useEffect(() => {
setActiveComponent(steps[activeStep]);
}, [activeStep]);
return (
<div>
<activeComponent.component {...activeComponent.config} />
{
isFinalStep ? (
<div className={classes.buttonsContainer}>
<div className={classes.buttons}>
<Button disabled={activeStep === 0} onClick={handleBack}>
Back
</Button>
<Button
variant="contained"
color="primary"
onClick={props.finishFunction}
>
Finish And Submit
</Button>
</div>
</div>
)
:
null
}
</div>
);
};
From your image, every form looks the same. You should try providing a unique key value to your component so React know that each form is different. In this case it can be the step number for example:
<activeComponent.component {...props} key='form-step-1'>
Lately i discovered the react-hook-from plugin which on the first sight seem to be perfect to start using and replace other plugins due to its great performance.
After using the plugin for some really simple forms i came across a complicated form that i wish to handle with the plugin. My form is based on a nested object which has the following structure. (Typescript definition)
type model = {
tag: string;
visible: boolean;
columns?: (modelColumn | model)[];
}
type modelColumn = {
property: string;
}
So in order to handle to handle a n-level nested form i created the following components.
const initialData = {
...
datasource: {
tag: "tag1",
visible: true,
columns: [
{
property: "property",
},
{
property: "property1",
},
{
tag: "tag2",
visible: true,
columns: [
{
property: "property",
},
{
tag: "tag3",
visible: false,
columns: [
{
property: "property",
}
]
}
]
},
{
entity: "tag4",
visible: false,
}
],
},
...
}
export const EditorContent: React.FunctionComponent<EditorContentProps> = (props: any) => {
const form = useForm({
mode: 'all',
defaultValues: initialData,
});
return (
<FormProvider {...form}>
<form>
<Handler
path="datasource"
/>
</form>
</FormProvider>
);
}
In the above component the main form is created loaded with initial data. (I provided an example value). The Handler component has the login of recursion with the path property to call nested logic of the form data type. Here is the sample implementation.
...
import { get, isNil } from "lodash";
import { useFieldArray, useForm, useFormContext, useWatch } from "react-hook-form";
...
export type HandlerProps = {
path: string;
index?: number;
...
}
export const Handler: React.FunctionComponent<HandlerProps> = (props) => {
const { path, index, onDelete, ...rest } = props;
const { control } = useFormContext();
const name = isNil(index)? `${path}` :`${path}[${index}]`;
const { fields, append, insert, remove } = useFieldArray({
control: control,
name: `${name}.columns`
});
...
const value = useWatch({
control,
name: `${name}`,
});
...
const addHandler = () => {
append({ property: null });
};
console.log(`Render path ${name}`);
return (
<React.Fragment>
<Row>
<Col>
<FormField name={`${name}.tag`} defaultValue={value.tag}>
<Input
label={`Tag`}
/>
</FormField>
</Col>
<Col>
<FormField defaultValue={value.visible} name={`${name}.visible`} >
<Switch />
</FormField>
</Col>
<Col>
<button onClick={addHandler}>Add Column Property</button>
</Col>
</Row>
{
fields && (
fields.map((field: any, _index: number) => {
if (field.property !== undefined) {
return (
<Column
path={`${name}.columns`}
index={_index}
control={control}
fields={fields}
onDelete={() => remove(_index) }
/>
)
} else {
return (
<Handler
path={`${name}.columns`}
index={_index}
/>
)
}
})
)
}
</React.Fragment>
);
}
Essentially the Handler Component uses the form's context and call itself if it should render a nested object of the form like datasource.columns[x] which the register to useFieldArray to get it's columns. Everything so far works fine. I render the complete tree (if i can say) like object form correctly.
For reference here is the code of the Column Component as also for the formField helper component FormField.
export const Column: React.FunctionComponent<ColumnProps> = (props) => {
const { fields, control, path, index, onDelete } = props;
const value = useWatch({
control,
name: `${path}[${index}]`,
defaultValue: !isNil(fields[index])? fields[index]: {
property: null,
}
});
console.log(`Render of Column ${path} ${value.property}`);
return (
<Row>
<Col>
<button onClick={onDelete}>Remove Property</button>
</Col>
<Col>
<FormField name={`${path}[${index}].property`} defaultValue={value.property}>
<Input
label={`Column Property name`}
/>
</FormField>
</Col>
</Row>
);
}
export type FormFieldProps = {
name: string;
disabled?: boolean;
validation?: any;
defaultValue?: any;
children?: any;
};
export const FormField: React.FunctionComponent<FormFieldProps> = (props) => {
const {
children,
name,
validation,
defaultValue,
disabled,
} = props;
const { errors, control, setValue } = useFormContext();
return (
<Controller
name={name}
control={control}
defaultValue={defaultValue? defaultValue: null}
rules={{ required: true }}
render={props => {
return (
React.cloneElement(children, {
...children.props,
...{
disabled: disabled || children.props.disabled,
value: props.value,
onChange: (v:any) => {
props.onChange(v);
setValue(name, v, { shouldValidate: true });
},
}
})
);
}}
/>
);
}
The problem is when i remove a field from the array. The Handler component re-renders having correct values on the fields data. But the values of useWatch have the initial data which leads to a wrong render with wrong fields being displayed an the forms starts to mesh up.
Is there any advice on how to render such a nested form the correct way. I guess this is not an issue from the react-hook-form but the implementation has an error which seems to cause the problem.
I am trying to manage state with react hooks for my checkBOx toggle but I am failing to manage different state for different checkbox, with my code I am trying "ON" & "OFF" for toggle but when I am trying to toggle any checkbox its changing states for all of my checkboxes all at once.
following is my code :
import React,{useState} from 'react';
import { Box, Checkbox, Flex, Table, Txt } from 'rendition';
import styled from 'styled-components';
const ControlContainer = styled(Box)`
border-top-left-radius: 10px;
`;
export const Devices = () => {
const [checked, setChecked] = useState(true);
function toggle(event) {
return setChecked(event.target.checked ? setChecked(false) : setChecked(true));
}
const SAMPLE_DATA = [
{
id: 1,
name: 'Balcony',
active: true,
brightness: 50,
},
{
id: 2,
name: 'Bedroom 01',
active: false,
brightness: 70,
},
{
id: 3,
name: 'Bedroom 02',
active: false,
brightness: 70,
},
];
const columns = [
{
field: 'name',
label: 'Room',
sortable: true,
},
{
field: 'active',
label: 'State',
sortable: true,
render() {
return (
<Flex>
<Checkbox toggle checked={checked} onChange={toggle} mr={2} />
<Txt ml={2}>{checked ? 'On' : 'Off'}</Txt>
</Flex>
);
},
},
{
field: 'brightness',
label: 'Brightness',
sortable: true,
render(value) {
return `${value}%`;
},
},
];
return (
<Flex flex='1' mt={4}>
<Box flex='3' pl={3}>
<Table
flex='1'
columns={columns}
data={SAMPLE_DATA}
rowKey='id'
onRowClick={console.log}
/>
</Box>
</Flex>
);
};
PS : Getting following warnings :
Each child in a list should have a unique key prop.
Failed prop type: You provided a checked prop to a form field without an
onChange handler. This will render a read-only field. If the field should be
mutable use defaultChecked. Otherwise, set either onChange or readOnly.
So, after getting lots of suggestions and a downvote finally I got a solution.
It is divided into three parts.
first I passed SAMPLE_DATA as useState
const [data,setdata]=useState(SAMPLE_DATA)
Secondly, I made some changes in my checkbox by adding a check on the field : row.active
<Flex>
<Checkbox toggle
checked={row.active}
mr={2} id={`checkbox-${row.id}`}
data-rowid={row.id}
onChange={toggle}/>
<Txt ml={2}> {row.active ? 'on' : 'off'} </Txt>
</Flex>
at last, I altered my toggle function accordingly
const toggle=(event)=> {
let rowId = event.target.getAttribute('data-rowId')
let newData = data.map(item => {
if(item.id == rowId) {
item.active = event.target.checked
}
return item;
})
setdata(newData)
}
Thank you all for your valuable time and suggestions.Topic Closed