Skip to content

Commit

Permalink
Fix primefaces#3790: SelectButton focus method
Browse files Browse the repository at this point in the history
  • Loading branch information
melloware committed Dec 16, 2022
1 parent 9d37860 commit 17cf5b7
Show file tree
Hide file tree
Showing 4 changed files with 180 additions and 7 deletions.
159 changes: 159 additions & 0 deletions components/doc/selectbutton/validationdoc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import { useState } from 'react';
import { Controller, useForm } from 'react-hook-form';
import { Button } from '../../lib/button/Button';
import { SelectButton } from '../../lib/selectbutton/SelectButton';
import { classNames } from '../../lib/utils/Utils';
import { DocSectionCode } from '../common/docsectioncode';
import { DocSectionText } from '../common/docsectiontext';

export function ValidationDoc(props) {
const options = ['Off', 'On'];
const [formData, setFormData] = useState({});
const defaultValues = { engine: '' };
const form = useForm({ defaultValues });
const errors = form.formState.errors;

const onSubmit = (data) => {
setFormData(data);
};

const getFormErrorMessage = (name) => {
return errors[name] && <small className="p-error">{errors[name].message}</small>;
};

const code = {
basic: `
<Controller name="firstname" control={form.control} rules={{ required: 'First Name is required.'}}
render={({ field, fieldState }) => (
<>
<label htmlFor={field.name} className={classNames({ 'p-error': errors.name })}>First Name*</label>
<InputText id={field.name} {...field} className={classNames({ 'p-invalid': fieldState.error })} />
{getFormErrorMessage(field.name)}
</>
)}
/>
`,
javascript: `
import { useState } from 'react';
import { Controller, useForm } from 'react-hook-form';
import { Button } from 'primereact/button';
import { classNames } from 'primereact/utils';
import { InputText } from "primereact/inputtext";
export default function ValidationDemo() {
const [formData, setFormData] = useState({});
const defaultValues = {firstname: ''};
const form = useForm({ defaultValues });
const errors = form.formState.errors;
const onSubmit = (data) => {
setFormData(data);
};
const getFormErrorMessage = (name) => {
return errors[name] && <small className="p-error">{errors[name].message}</small>
};
return (
<form onSubmit={form.handleSubmit(onSubmit)} className="p-fluid">
<div className="field">
<Controller
name="firstname"
control={form.control}
rules={{ required: 'First Name is required.' }}
render={({ field, fieldState }) => (
<>
<label htmlFor={field.name} className={classNames({ 'p-error': errors.firstname })}>
First Name*
</label>
<InputText id={field.name} {...field} className={classNames({ 'p-invalid': fieldState.error })} />
{getFormErrorMessage(field.name)}
</>
)}
/>
</div>
<Button label="Submit" type="submit" icon="pi pi-check" />
</form>
)
}
`,
typescript: `
import { useState } from 'react';
import { Controller, useForm } from 'react-hook-form';
import { Button } from 'primereact/button';
import { classNames } from 'primereact/utils';
import { InputText } from "primereact/inputtext";
export default function InvalidDemo() {
const [formData, setFormData] = useState<any>({});
const defaultValues = {firstname: ''};
const form = useForm({ defaultValues });
const errors = form.formState.errors;
const onSubmit = (data: any) => {
setFormData(data);
};
const getFormErrorMessage = (name: string) => {
return errors[name] && <small className="p-error">{errors[name].message}</small>
};
return (
<form onSubmit={form.handleSubmit(onSubmit)} className="p-fluid">
<div className="field">
<Controller
name="firstname"
control={form.control}
rules={{ required: 'First Name is required.' }}
render={({ field, fieldState }) => (
<>
<label htmlFor={field.name} className={classNames({ 'p-error': errors.firstname })}>
First Name*
</label>
<InputText id={field.name} {...field} className={classNames({ 'p-invalid': fieldState.error })} />
{getFormErrorMessage(field.name)}
</>
)}
/>
</div>
<Button label="Submit" type="submit" icon="pi pi-check" />
</form>
)
}
`
};

return (
<>
<DocSectionText {...props}>
<p>
<a href="https://react-hook-form.com/">React Hook Form</a> is the most popular React library for form validation. The field will be highlighted and receive focus on validation failure.
</p>
</DocSectionText>
<div className="card flex justify-content-center">
<div className="flex flex-column gap-2">
<form onSubmit={form.handleSubmit(onSubmit)} className="p-fluid">
<div className="field">
<Controller
name="engine"
control={form.control}
rules={{ required: 'Engine State is required.' }}
render={({ field, fieldState }) => (
<>
<label htmlFor={field.name} className={classNames({ 'p-error': errors.engine })}>
Engine State*
</label>
<SelectButton id={field.name} options={options} {...field} className={classNames({ 'p-invalid': fieldState.error })} />
{getFormErrorMessage(field.name)}
</>
)}
/>
</div>
<Button label="Submit" type="submit" icon="pi pi-check" />
</form>
</div>
</div>
<DocSectionCode code={code} />
</>
);
}
11 changes: 9 additions & 2 deletions components/lib/selectbutton/SelectButton.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as React from 'react';
import { Tooltip } from '../tooltip/Tooltip';
import { classNames, ObjectUtils } from '../utils/Utils';
import { classNames, DomHandler, ObjectUtils } from '../utils/Utils';
import { SelectButtonItem } from './SelectButtonItem';

export const SelectButton = React.memo(
Expand Down Expand Up @@ -90,9 +90,16 @@ export const SelectButton = React.memo(
return null;
};

const focus = () => {
const button = DomHandler.findSingle(elementRef.current, '[tabindex]:not([tabindex="-1"]):not([disabled])');

button && button.focus();
};

React.useImperativeHandle(ref, () => ({
props,
getElement: () => elementRef.current
getElement: () => elementRef.current,
focus
}));

const hasTooltip = ObjectUtils.isNotEmpty(props.tooltip);
Expand Down
3 changes: 2 additions & 1 deletion components/lib/selectbutton/selectbutton.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as React from 'react';
import TooltipOptions from '../tooltip/tooltipoptions';
import { SelectItemOptionsType } from '../selectitem/selectitem';
import TooltipOptions from '../tooltip/tooltipoptions';

type SelectButtonOptionDisabledType = string | ((option: any) => boolean);

Expand Down Expand Up @@ -39,4 +39,5 @@ export interface SelectButtonProps extends Omit<React.DetailedHTMLProps<React.In

export declare class SelectButton extends React.Component<SelectButtonProps, any> {
public getElement(): HTMLDivElement;
public focus(): void;
}
14 changes: 10 additions & 4 deletions pages/selectbutton/index.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import Head from 'next/head';
import { DocActions } from '../../components/doc/common/docactions';
import { DocSectionNav } from '../../components/doc/common/docsectionnav';
import { DocSections } from '../../components/doc/common/docsections';
import { DocActions } from '../../components/doc/common/docactions';
import { ImportDoc } from '../../components/doc/selectbutton/importdoc';
import { ApiDoc } from '../../components/doc/selectbutton/apidoc';
import { BasicDoc } from '../../components/doc/selectbutton/basicdoc';
import { MultipleSelectionDoc } from '../../components/doc/selectbutton/multipleselectiondoc';
import { CustomContentDoc } from '../../components/doc/selectbutton/customcontentdoc';
import { ApiDoc } from '../../components/doc/selectbutton/apidoc';
import { ImportDoc } from '../../components/doc/selectbutton/importdoc';
import { MultipleSelectionDoc } from '../../components/doc/selectbutton/multipleselectiondoc';
import { ValidationDoc } from '../../components/doc/selectbutton/validationdoc';

const SelectButtonDemo = () => {
const docs = [
Expand All @@ -30,6 +31,11 @@ const SelectButtonDemo = () => {
label: 'Custom Content',
component: CustomContentDoc
},
{
id: 'validation',
label: 'Validation',
component: ValidationDoc
},
{
id: 'api',
label: 'API',
Expand Down

0 comments on commit 17cf5b7

Please sign in to comment.