Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat : integrated form composer with Searchable dropdown #1258

Open
wants to merge 2 commits into
base: sandbox-develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions micro-frontends/sandbox-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"react-dom": "18.3.1",
"react-i18next": "15.0.0",
"react-router-dom": "6.25.1",
"react-select": "^5.8.0",
"webpack-dev-server": "^5.0.4"
},
"devDependencies": {
Expand Down
80 changes: 63 additions & 17 deletions micro-frontends/sandbox-ui/packages/components/src/CustomWidgets.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,37 @@
import React from 'react';
import Select from 'react-select';

export const CustomDatePicker = ({ value, onChange, label }) => {
const handleChange = (event) => {
const date = event.target.value;
onChange(date ? new Date(date).toISOString() : '');
};

const inputStyle = {
width: '100%',
boxSizing: 'border-box',
paddingRight: '0.75rem', // Add padding to create space for the date icon
outline: '0.125rem solid transparent',
outlineOffset: '0.125rem',
height: '2.5rem',
backgroundColor: '#fff',
fontStyle: 'normal',
fontFamily: 'Roboto',
fontSize: '1rem',
border: '0.063rem solid #787878',
color: '#363636',
lineHeight: '1.5rem'
};

return (
<div style={{ marginBottom: '10px' }}>
{label && <label style={{ display: 'block', marginBottom: '5px' }}>{label}</label>}
<div style={{ marginBottom: '1rem' }}>
{label && <label style={{ display: 'block', marginBottom: '0.5rem' }}>{label}</label>}
<input
className='digit-citizenCard-input'
type='date'
value={value ? value.split('T')[0] : ''}
onChange={handleChange}
style={inputStyle} // Apply the custom style
/>
</div>
);
Expand All @@ -22,24 +40,52 @@ export const CustomDatePicker = ({ value, onChange, label }) => {

export const CustomDropdown = ({ options, value, onChange, label }) => {

const handleChange = (event) => {
onChange(event.target.value);
const handleChange = (selectedOption) => {
onChange(selectedOption.value);
};

const customStyles = {
control: (base) => ({
...base,
width: '100%',
boxSizing: 'border-box',
paddingLeft: 0,
outline: '0.125rem solid transparent',
outlineOffset: '0.125rem',
height: '2.5rem',
backgroundColor: '#fff',
fontStyle: 'normal',
fontFamily: 'Roboto',
fontSize: '1rem',
border: '0.063rem solid #787878',
color: '#363636',
lineHeight: '1.5rem',
borderRadius: 0,
}),
valueContainer: (base) => ({
...base
})
};

// Convert options to the format expected by react-select
const formattedOptions = options.map(option => ({
value: option.value,
label: option.label
}));

// Find the currently selected option
const selectedValue = formattedOptions.find(option => option.value === value);

return (
<div style={{ marginBottom: '10px' }}>
{label && <label style={{ display: 'block', marginBottom: '5px' }}>{label}</label>}
<select
className='digit-citizenCard-input'
value={value}
<div style={{ marginBottom: '1rem' }}>
{label && <label style={{ display: 'block', marginBottom: '0.5rem' }}>{label}</label>}
<Select
// className='digit-citizenCard-input'
value={selectedValue}
onChange={handleChange}
>
{options.map((option, index) => (
<option key={index} value={option.value}>
{option.label}
</option>
))}
</select>
options={formattedOptions}
styles={customStyles} // Apply the custom styles
/>
</div>
);
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react';

export const CheckBox = ({ checked, onChange, label }) => {
return (
<label style={{ display: 'flex', flexDirection: 'column', alignItems: 'start' }}>
{label}
<input
type="checkbox"
checked={checked}
onChange={onChange}
style={{ width: '2vh', height: '2vw' }}
/>
</label>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React from 'react';
import Select from 'react-select';


export const EnumBasedDropdown = ({ options, value, onChange }) => {
const handleChange = (selectedOption) => {
onChange(selectedOption.value);
};

const customStyles = {
control: (base) => ({
...base,
width: '100%',
boxSizing: 'border-box',
paddingLeft: 0,
outline: '0.125rem solid transparent',
outlineOffset: '0.125rem',
height: '2.5rem',
backgroundColor: '#fff',
fontStyle: 'normal',
fontFamily: 'Roboto',
fontSize: '1rem',
border: '0.063rem solid #787878',
color: '#363636',
lineHeight: '1.5rem',
borderRadius: 0,
}),
valueContainer: (base) => ({
...base
})
};

// Convert enum values to the format expected by react-select
const formattedOptions = options.map(option => ({
value: option,
label: option
}));

// Find the currently selected option
const selectedValue = formattedOptions.find(option => option.value === value);

return (
<div style={{ marginBottom: '1rem' }}>
<Select
value={selectedValue}
onChange={handleChange}
options={formattedOptions}
styles={customStyles} // Apply the custom styles
/>
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,75 +3,14 @@ import { useForm, Controller, useFieldArray, useWatch } from "react-hook-form";
import "react-tabs/style/react-tabs.css";
import { getUpdatedUISchema } from "./formTabUtils";
import Stepper from "react-stepper-horizontal";
import useLastUpdatedField from "../../../hooks/useLastUpdatedField";
import DigitUIComponents from "../../../DigitUIComponents";
import useLastUpdatedField from "../../hooks/useLastUpdatedField";
import DigitUIComponents from "../../DigitUIComponents";
import { EnumBasedDropdown } from "./EnumBasedDropdown";
import { CheckBox } from "./CheckBox";

const { TextInput, Button } = DigitUIComponents;

/**
* @author jagankumar-egov
* @date 2024-08-01
* @description This file contains the FormComposer component and related subcomponents for building dynamic forms using React Hook Form and custom widgets.
*/



/**
* dropdown component
*/
const EnumBasedDropdown = ({ options, value, onChange }) => {
const handleChange = (event) => {
onChange(event.target.value);
};

return (
<div style={{ marginBottom: '10px' }}>
<select
className='digit-citizenCard-input'
value={value}
onChange={handleChange}
>
{options.map((option, index) => (
<option key={index} value={option}>
{option}
</option>
))}
</select>
</div>
);
};

/**
* CheckBox
*/
const CheckBox = ({ checked, onChange, label }) => {
return (
<label style={{ display: 'flex', flexDirection: 'column', alignItems: 'start' }}>
{label}
<input
type="checkbox"
checked={checked}
onChange={onChange}
style={{ width: '2vh', height: '2vw' }}
/>
</label>
);
};

/**
* RenderIndividualField - Renders an individual field based on its configuration.
*
* @param {Object} props - The component props.
* @param {string} props.name - The name of the field.
* @param {Object} props.property - The property configuration for the field.
* @param {string} props.uiWidget - The custom widget type for rendering.
* @param {Object} props.control - The control object from react-hook-form.
* @param {Object} props.errors - The form errors object from react-hook-form.
* @param {Object} props.customWidgets - A dictionary of custom widgets.
*
*
* @returns {React.ReactNode} The rendered field component.
*/
const RenderIndividualField = React.memo(
({ name, property, uiWidget, control, errors, customWidgets }) => {
const CustomWidget = customWidgets[uiWidget];
Expand Down Expand Up @@ -311,7 +250,6 @@ const RenderField = ({
}
return null;
}
console.log(property, " pppppppppppppppppp")
if (property.type === "array") {
return (
<RenderArrayField
Expand Down Expand Up @@ -375,7 +313,7 @@ const RenderField = ({
* @author jagankumar-egov
* @returns {React.ReactNode} The rendered form component.
*/
const FormComposer = ({ schema, uiSchema, customWidgets,onSubmit,onError,submitHandler=false }) => {
const FormComposer = ({ schema, uiSchema, customWidgets, onSubmit, onError, submitHandler = false }) => {
const [currentTab, setCurrentTab] = useState(0);

const buttonStyles = {
Expand Down Expand Up @@ -531,28 +469,28 @@ const FormComposer = ({ schema, uiSchema, customWidgets,onSubmit,onError,submitH
)}
{submitHandler && submitHandler}
<div style={{ display: 'flex', justifyContent: 'center', marginTop: '20px', flexWrap: 'wrap' }}>
{!(uiLayout?.hideTabNavigateButtons)&& <>
{currentTab > 0 && (
<Button
label="Previous"
onClick={() => setCurrentTab((prev) => prev - 1)}
style={buttonStyles}
/>
)}
{currentTab < uiLayout?.layouts.length - 1 && (
<Button
label="Next"
onClick={() => setCurrentTab((prev) => prev + 1)}
style={buttonStyles}
/>
)}
{currentTab === uiLayout?.layouts.length - 1 && (
<Button
label="Submit"
type="submit"
style={buttonStyles}
/>
)}
{!(uiLayout?.hideTabNavigateButtons) && <>
{currentTab > 0 && (
<Button
label="Previous"
onClick={() => setCurrentTab((prev) => prev - 1)}
style={buttonStyles}
/>
)}
{currentTab < uiLayout?.layouts.length - 1 && (
<Button
label="Next"
onClick={() => setCurrentTab((prev) => prev + 1)}
style={buttonStyles}
/>
)}
{currentTab === uiLayout?.layouts.length - 1 && (
<Button
label="Submit"
type="submit"
style={buttonStyles}
/>
)}
Comment on lines +472 to +493
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider extracting button rendering logic for better readability.

The logic for rendering navigation buttons is repeated. Consider extracting it into a separate function or component to reduce duplication and improve readability.

const renderNavigationButtons = (currentTab, uiLayout, buttonStyles, setCurrentTab) => (
  <>
    {currentTab > 0 && (
      <Button
        label="Previous"
        onClick={() => setCurrentTab((prev) => prev - 1)}
        style={buttonStyles}
      />
    )}
    {currentTab < uiLayout?.layouts.length - 1 && (
      <Button
        label="Next"
        onClick={() => setCurrentTab((prev) => prev + 1)}
        style={buttonStyles}
      />
    )}
    {currentTab === uiLayout?.layouts.length - 1 && (
      <Button
        label="Submit"
        type="submit"
        style={buttonStyles}
      />
    )}
  </>
);

</>}
</div>
</div>
Expand Down Expand Up @@ -603,28 +541,28 @@ const FormComposer = ({ schema, uiSchema, customWidgets,onSubmit,onError,submitH
)}
{submitHandler && submitHandler}
<div style={{ display: 'flex', justifyContent: 'center', marginTop: '20px', flexWrap: 'wrap' }}>
{!(uiLayout?.hideTabNavigateButtons)&& <>
{currentTab > 0 && (
<Button
label="Previous"
onClick={() => setCurrentTab((prev) => prev - 1)}
style={buttonStyles}
/>
)}
{currentTab < uiLayout?.layouts.length - 1 && (
<Button
label="Next"
onClick={() => setCurrentTab((prev) => prev + 1)}
style={buttonStyles}
/>
)}
{currentTab === uiLayout?.layouts.length - 1 && (
<Button
label="Submit"
type="submit"
style={buttonStyles}
/>
)}
{!(uiLayout?.hideTabNavigateButtons) && <>
{currentTab > 0 && (
<Button
label="Previous"
onClick={() => setCurrentTab((prev) => prev - 1)}
style={buttonStyles}
/>
)}
{currentTab < uiLayout?.layouts.length - 1 && (
<Button
label="Next"
onClick={() => setCurrentTab((prev) => prev + 1)}
style={buttonStyles}
/>
)}
{currentTab === uiLayout?.layouts.length - 1 && (
<Button
label="Submit"
type="submit"
style={buttonStyles}
/>
)}
</>}
</div>
</div>
Expand All @@ -642,8 +580,8 @@ const FormComposer = ({ schema, uiSchema, customWidgets,onSubmit,onError,submitH
control,
errors
)}
{!submitHandler&& <button type="submit">Submit</button>}
{submitHandler && submitHandler}
{!submitHandler && <button type="submit">Submit</button>}
{submitHandler && submitHandler}
</>
)}
</form>
Expand Down