Skip to main content

Attribute Selection Block

The AttributeSelection Component is a fully customizable and accessible attribute selection form built with React and TypeScript. It provides a complete interface for attribute selection with form validation, error handling, and modern design patterns.


🚀 Installation

npm install @nodeblocks/frontend-attribute-selection-block@0.2.0

📖 Usage

import {AttributeSelection} from '@nodeblocks/frontend-attribute-selection-block';
Live Editor

function BasicAttributeSelection() {
  const [submittedData, setSubmittedData] = useState(null);

  const handleSubmit = (data) => {
    console.log('Form submitted:', data);
    setSubmittedData(data);
  };

  const handleCancel = (reset) => {
    console.log('Form cancelled');
    reset();
    setSubmittedData(null);
  };

  const handleChange = (_setError, getValues) => {
    console.log('Form changed:', getValues());
  };

  return (
    <div>
      <AttributeSelection
        onSubmit={handleSubmit}
        onCancel={handleCancel}
        onChange={handleChange}
        defaultValues={{admin: false, editor: true, viewer: false}}
      >
        <AttributeSelection.Title>Select User Roles</AttributeSelection.Title>
        <AttributeSelection.Subtitle>Choose the roles to assign</AttributeSelection.Subtitle>
        <AttributeSelection.FieldName>Roles</AttributeSelection.FieldName>
        <AttributeSelection.Checkbox name="admin" label="Administrator" value="admin" />
        <AttributeSelection.Checkbox name="editor" label="Editor" value="editor" />
        <AttributeSelection.Checkbox name="viewer" label="Viewer" value="viewer" />
        <AttributeSelection.SubmitButton>Save Roles</AttributeSelection.SubmitButton>
        <AttributeSelection.CancelButton>Cancel</AttributeSelection.CancelButton>
      </AttributeSelection>

      {submittedData && (
        <div style={{marginTop: '16px', padding: '12px', background: '#f0f0f0', borderRadius: '8px'}}>
          <strong>Submitted Data:</strong>
          <pre>{JSON.stringify(submittedData, null, 2)}</pre>
        </div>
      )}
    </div>
  );
}
Result
Loading...

🔧 Props Reference

Main Component Props

The component accepts two type parameters: AttributeSelection<T, B> where:

  • T - Form data type (must extend Record<string, unknown> with index signature)
  • B - Blocks type (Record<string, never> for simple usage, Record<string, ReactNode> for block override)
PropTypeDefaultDescription
onSubmit(data: T) => voidRequiredCallback function triggered when form is submitted with valid data
onChange(setError: UseFormSetError<T>, getValues: UseFormGetValues<T>) => voidRequiredCallback function triggered when form values change
onCancel(reset: UseFormReset<T>) => voidRequiredCallback function triggered when cancel button is clicked
defaultValuesDefaultValues<T>undefinedDefault form values to populate fields on initial render
classNamestringundefinedAdditional CSS class name for styling the form container
childrenBlocksOverrideundefinedCustom blocks override for form components

Note: The main component extends MUI StackProps and renders as a form element using component="form". Default styling includes a Stack layout.

Sub-Components

The AttributeSelection component provides several sub-components. All sub-components receive their default values from the main component's context and can override these values through props.

AttributeSelection.Title

PropTypeDefaultDescription
childrenReactNode"職種"Title text content for the form header
variantTypographyProps['variant']"h4"MUI Typography variant
componentElementType"h2"HTML element to render
classNamestringundefinedAdditional CSS class name for styling

Note: This component inherits MUI Typography props. Default styling includes textAlign: 'center'.

AttributeSelection.Subtitle

PropTypeDefaultDescription
childrenReactNode"ご自身の職種を選択してください"Subtitle text content providing form instructions
variantTypographyProps['variant']"body2"MUI Typography variant
classNamestringundefinedAdditional CSS class name for styling

Note: This component inherits MUI Typography props. Default styling includes fontWeight: 600.

AttributeSelection.FieldName

PropTypeDefaultDescription
childrenReactNode"役割"Field name text content displayed above form inputs
variantTypographyProps['variant']"body2"MUI Typography variant
classNamestringundefinedAdditional CSS class name for styling

Note: This component inherits MUI Typography props. Default styling includes fontWeight: 600.

AttributeSelection.Checkbox

PropTypeDefaultDescription
namestring""Field name used for form data and validation
labelstring""Label text displayed next to the checkbox
valuestring""Value identifier for the checkbox option
classNamestringundefinedAdditional CSS class name for styling

Note: This component inherits MUI FormControlLabel props. Uses react-hook-form Controller for form integration. Default styling includes bordered container with rounded corners. The default checked state is controlled via the defaultValues prop on the main component.

AttributeSelection.SubmitButton

PropTypeDefaultDescription
childrenReactNode"次へ"Button text content
variantButtonProps['variant']"contained"MUI Button variant
sizeButtonProps['size']"large"MUI Button size
fullWidthbooleantrueWhether button spans full width
type"submit" | "button" | "reset""submit"HTML button type
disabledboolean!formState.isValidDisabled when form is invalid
classNamestringundefinedAdditional CSS class name for styling

Note: This component inherits MUI Button props. Automatically disabled when form validation fails.

AttributeSelection.CancelButton

PropTypeDefaultDescription
childrenReactNode"戻る"Button text content
variantButtonProps['variant']"outlined"MUI Button variant
sizeButtonProps['size']"large"MUI Button size
fullWidthbooleantrueWhether button spans full width
type"submit" | "button" | "reset""button"HTML button type
classNamestringundefinedAdditional CSS class name for styling

Note: This component inherits MUI Button props (except onClick). Calls onCancel(reset) when clicked.


🎨 Configuration examples

Custom Title Styling

<AttributeSelection.Title 
variant="h3"
sx={{
color: 'primary.main',
fontWeight: 700,
textAlign: 'left'
}}
/>

Custom Checkbox Styling

<AttributeSelection.Checkbox
name="option1"
label="Custom Option"
value="option1"
sx={{
border: '2px solid #3b82f6',
borderRadius: '12px',
'&:hover': { backgroundColor: '#f0f9ff' }
}}
/>

Custom Button Styling

<AttributeSelection.SubmitButton
variant="contained"
sx={{
background: 'linear-gradient(135deg, #0f172a 0%, #1e293b 100%)',
borderRadius: '12px',
py: 1.5
}}
>
Continue
</AttributeSelection.SubmitButton>

Using Block Override Pattern

<AttributeSelection {...props}>
{({defaultBlocks, defaultBlockOrder}) => {
return {
blocks: {
...defaultBlocks,
customInfo: <Alert severity="info">Select at least one option</Alert>,
},
blockOrder: ['title', 'subtitle', 'customInfo', 'fieldName', 'checkbox', 'submitButton', 'cancelButton']
};
}}
</AttributeSelection>

🔧 TypeScript Support

Full TypeScript support with comprehensive type definitions:

import {AttributeSelection} from '@nodeblocks/frontend-attribute-selection-block';
import {StackProps} from '@mui/material';
import {DefaultValues, UseFormGetValues, UseFormReset, UseFormSetError} from 'react-hook-form';
import {ReactNode} from 'react';

// Form data interface must include index signature for react-hook-form compatibility
interface CustomAttributeFormData {
[key: string]: unknown;
role?: boolean;
department?: boolean;
experience?: boolean;
skills?: boolean;
}

// Main component props interface with two type parameters:
// T - Form data type
// B - Blocks type (Record<string, never> for simple usage, Record<string, ReactNode> for block override)
interface AttributeSelectionProps<T extends Record<string, unknown>, B extends Record<string, ReactNode>>
extends Omit<StackProps, 'onSubmit' | 'onChange' | 'children'> {
onSubmit: (data: T) => void;
onCancel: (reset: UseFormReset<T>) => void;
onChange: (setError: UseFormSetError<T>, getValues: UseFormGetValues<T>) => void;
defaultValues?: DefaultValues<T>;
children?: BlocksOverride;
}

// Type-safe submit handler
const handleSubmit = (formData: CustomAttributeFormData): void => {
console.log(formData.role, formData.department, formData.experience);
};

// Type-safe change handler with validation
const handleChange = (
_setError: UseFormSetError<CustomAttributeFormData>,
getValues: UseFormGetValues<CustomAttributeFormData>,
): void => {
const values = getValues();
const hasSelection = values.role || values.department || values.experience || values.skills;
if (!hasSelection) {
_setError('role', {message: 'Please select at least one option'});
}
};

// Type-safe cancel handler
const handleCancel = (reset: UseFormReset<CustomAttributeFormData>): void => {
reset();
console.log('Form cancelled');
};

// Simple usage with two type parameters
const SimpleAttributeSelectionForm = () => {
return (
<AttributeSelection<CustomAttributeFormData, Record<string, never>>
onSubmit={handleSubmit}
onChange={handleChange}
onCancel={handleCancel}
defaultValues={{role: false, department: false, experience: false, skills: false}}
>
<AttributeSelection.Title>Select Your Attributes</AttributeSelection.Title>
<AttributeSelection.Subtitle>Choose the options that apply to you</AttributeSelection.Subtitle>
<AttributeSelection.FieldName>Professional Information</AttributeSelection.FieldName>
<AttributeSelection.Checkbox name="role" label="Management Role" value="role" />
<AttributeSelection.Checkbox name="department" label="Technical Department" value="department" />
<AttributeSelection.Checkbox name="experience" label="5+ Years Experience" value="experience" />
<AttributeSelection.Checkbox name="skills" label="Leadership Skills" value="skills" />
<AttributeSelection.SubmitButton>Continue</AttributeSelection.SubmitButton>
<AttributeSelection.CancelButton>Go Back</AttributeSelection.CancelButton>
</AttributeSelection>
);
};

// Block override pattern with ReactNode blocks type
const AdvancedAttributeSelectionForm = () => {
return (
<AttributeSelection<CustomAttributeFormData, Record<string, ReactNode>>
onSubmit={handleSubmit}
onChange={handleChange}
onCancel={handleCancel}
defaultValues={{role: false, department: false, experience: false, skills: false}}
>
{({defaultBlocks, defaultBlockOrder}) => {
return {
blocks: {
...defaultBlocks,
title: (
<AttributeSelection.Title sx={{color: 'primary.main'}}>
Custom Title
</AttributeSelection.Title>
),
},
blockOrder: defaultBlockOrder,
};
}}
</AttributeSelection>
);
};

📝 Notes

  • The root component uses MUI's Stack with component="form" to create a semantic form element
  • Form state management is handled by react-hook-form with mode: 'onBlur' for validation
  • The onChange callback is triggered whenever form values change, allowing real-time validation
  • SubmitButton is automatically disabled when formState.isValid is false
  • CancelButton calls the onCancel callback with the form reset function as argument
  • Default block order: title, subtitle, fieldName, checkbox, submitButton, cancelButton
  • All sub-components support the MUI sx prop for inline styling
  • Checkboxes use react-hook-form Controller for form state integration
  • The component supports two generic type parameters: <FormData, BlocksType> where FormData defines form values and BlocksType controls the blocks structure
  • Form data interfaces must include [key: string]: unknown index signature for react-hook-form compatibility
  • Use Record<string, never> as the second type parameter for simple usage, and Record<string, ReactNode> for block override pattern
  • CSS classes follow BEM naming: nbb-attribute-selection, nbb-attribute-selection-title, etc.

Built with ❤️ using React, TypeScript, and MUI.