2.3. Create a Custom React Component
Now, we can actually start creating our custom buton! To do so, we will first create a folder for our React module code called CustomButton in /EZEXTEND_ROOT/ui/src/Components/Widgets/.
Create and open a new file CustomButton.js. EzeXtend uses Redux to manage complicated state in the application, so we will need to import the state selector from Redux:
Note
If this is your first time hearing about “state” in regards to React, Redux, or both, then you should take a moment to look into both of these softwares and the concept of state and how they handle them in greater depth, as this is an integral part of working with React and can be confusing if you are out of the loop.
1import { useSelector } from 'react-redux';
2import { Button } from '@mui/material';
We are taking the easy route with this button, and rather than recreating a button from scratch, we are instead using a pre-made React button provided in the Material library and we are wrapping it with our custom specifications. We need to define the functional component and provide it as an exportable default:
1...
2import { Button } from '@mui/material';
3
4function CustomButton({ id }) {
5
6}
7
8export default CustomButton;
Next, we will make our custom button return the pre-made button provided by the Material library. We will also provide some properties to the JSX element so that we can customize the button slightly:
1function CustomButton({ id }) {
2 return (
3 <Button sx={{height: '100%', width: '100%'}} variant='outlined'>
4
5 </Button>
6 )
7}
Here we are passing two props, sx and variant. The former allows us to define a JSON object containing a subset of CSS parameters that will be used to override the default CSS styling that comes with Material UI buttons. The latter is a property defined by Material, the button type or variant. In this case we are using the ‘outlined’ button variant.
We want the label for our button to be displayed within the button itself, so to do this we will need to get the label from our CustomButtonData from earlier. The crux here is that we will not be importing the button data directly, instead EzeXtend will load that custom data at runtime and make it available throughout the application via Redux. Because of this, we will have to load the data in from Redux and apply it to our label within the CustomButton module like so:
Note
The array evaluatedWidgetProperties at index id contains the JSON object value corresponding to the data key in the CustomButtonData object we defined.
1function CustomButton({ id }) {
2
3 const props = useSelector(
4 (store) => store.dashboard.evaluation.evaluatedWidgetProperties[id]
5 );
6
7 return (
8 <Button sx={{height: '100%', width: '100%'}} variant='outlined'>
9 { props.label }
10 </Button>
11 )
12}
Were we are providing useSelector() an anonymous function as an argument, knowing that that anonymous function will be provided the store that contains our state data.
Finally, we need to tell EzeXtend that our component exists. To do this we will modify /EZEXTEND_ROOT/ui/src/Components/Panel.js. First import the new component, and then in the function ComponentProvider(), we need to return the JSX for our new custom button in the case that it is selected from within the panel:
1...
2import Button from 'Components/Widgets/Button/Button';
3import CustomButton from 'Components/Widgets/CustomButton/CustomButton';
4import Radio from 'Components/Widgets/Radio/Radio';
5...
6function ComponentProvider(type, id) {
7 switch(type) {
8 case WidgetsMapping.CHARTS.COMBO:
9 return <Chart id={id} />;
10 case WidgetsMapping.INPUTS.BUTTON:
11 return <Button id={id} />;
12 case WidgetsMapping.INPUTS.CUSTOM_BUTTON:
13 return <CustomButon id={id} />;
14 case WidgetsMapping.INPUTS.RADIO:
15 return <Radio id={id} />;
16 ...
17 }
18}
Now, we can run the project in development mode once more. We should be able to click and drag the custom button from the sidebar entry onto the dashboard. This is shown in create-component.
A custom button widget displayed in the dashboard.