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

Updated pickers with input attribute compatibility. #33243

Open
wants to merge 16 commits into
base: master
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "fix: Added label, required, and error properties to BasePicker.",
"packageName": "@fluentui/react",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as React from 'react';
import { TextField } from '@fluentui/react/lib/TextField';
import { Checkbox } from '@fluentui/react/lib/Checkbox';
import { IPersonaProps, IPersonaStyles } from '@fluentui/react/lib/Persona';
import {
Expand Down Expand Up @@ -42,6 +43,9 @@ const personaStyles: Partial<IPersonaStyles> = {
export const PeoplePickerListExample: React.FunctionComponent = () => {
const [delayResults, setDelayResults] = React.useState(false);
const [isPickerDisabled, setIsPickerDisabled] = React.useState(false);
const [pickerLabel, setPickerLabel] = React.useState<string | undefined>('Choose People');
const [showPickerLabel, setShowPickerLabel] = React.useState(false);
const [isPickerRequired, setIsPickerRequired] = React.useState(false);
const [mostRecentlyUsed, setMostRecentlyUsed] = React.useState<IPersonaProps[]>(mru);
const [peopleList, setPeopleList] = React.useState<IPersonaProps[]>(people);

Expand Down Expand Up @@ -102,6 +106,18 @@ export const PeoplePickerListExample: React.FunctionComponent = () => {
setIsPickerDisabled(!isPickerDisabled);
};

const onShowLabelButtonClick = (): void => {
setShowPickerLabel(!showPickerLabel);
};

const onPickerLabelChange = (_: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, newValue?: string): void => {
setPickerLabel(newValue ?? '');
};

const onRequiredButtonClick = (): void => {
setIsPickerRequired(!isPickerRequired);
};

const onToggleDelayResultsChange = (): void => {
setDelayResults(!delayResults);
};
Expand All @@ -115,9 +131,17 @@ export const PeoplePickerListExample: React.FunctionComponent = () => {
);
};

const onGetErrorMessage = React.useCallback(
(items: IPersonaProps[]): string | JSX.Element | PromiseLike<string | JSX.Element> | undefined => {
return isPickerRequired && (items || []).length === 0 ? 'Please fill out this field.' : undefined;
},
[isPickerRequired],
);

return (
<div>
<ListPeoplePicker
label={showPickerLabel ? pickerLabel : undefined}
// eslint-disable-next-line react/jsx-no-bind
onResolveSuggestions={onFilterChanged}
// eslint-disable-next-line react/jsx-no-bind
Expand All @@ -141,6 +165,8 @@ export const PeoplePickerListExample: React.FunctionComponent = () => {
componentRef={picker}
resolveDelay={300}
disabled={isPickerDisabled}
required={isPickerRequired}
onGetErrorMessage={onGetErrorMessage}
/>
<Checkbox
label="Disable People Picker"
Expand All @@ -156,6 +182,28 @@ export const PeoplePickerListExample: React.FunctionComponent = () => {
onChange={onToggleDelayResultsChange}
styles={checkboxStyles}
/>
<Checkbox
label="Required People Picker"
checked={isPickerRequired}
// eslint-disable-next-line react/jsx-no-bind
onChange={onRequiredButtonClick}
styles={checkboxStyles}
/>
<Checkbox
label="Show Label"
checked={showPickerLabel}
// eslint-disable-next-line react/jsx-no-bind
onChange={onShowLabelButtonClick}
styles={checkboxStyles}
/>
{showPickerLabel && (
<TextField
label={'People Picker Label'}
value={pickerLabel}
// eslint-disable-next-line react/jsx-no-bind
onChange={onPickerLabelChange}
/>
)}
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as React from 'react';
import { TextField } from '@fluentui/react/lib/TextField';
import { Checkbox } from '@fluentui/react/lib/Checkbox';
import { IPersonaProps } from '@fluentui/react/lib/Persona';
import {
Expand Down Expand Up @@ -29,6 +30,9 @@ const checkboxStyles = {
export const PeoplePickerNormalExample: React.FunctionComponent = () => {
const [delayResults, setDelayResults] = React.useState(false);
const [isPickerDisabled, setIsPickerDisabled] = React.useState(false);
const [pickerLabel, setPickerLabel] = React.useState<string | undefined>('Choose People');
const [showPickerLabel, setShowPickerLabel] = React.useState(false);
const [isPickerRequired, setIsPickerRequired] = React.useState(false);
const [showSecondaryText, setShowSecondaryText] = React.useState(false);
const [mostRecentlyUsed, setMostRecentlyUsed] = React.useState<IPersonaProps[]>(mru);
const [peopleList, setPeopleList] = React.useState<IPersonaProps[]>(people);
Expand Down Expand Up @@ -103,6 +107,18 @@ export const PeoplePickerNormalExample: React.FunctionComponent = () => {
setIsPickerDisabled(!isPickerDisabled);
};

const onShowLabelButtonClick = (): void => {
setShowPickerLabel(!showPickerLabel);
};

const onPickerLabelChange = (_: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, newValue?: string): void => {
setPickerLabel(newValue ?? '');
};

const onRequiredButtonClick = (): void => {
setIsPickerRequired(!isPickerRequired);
};

const onToggleDelayResultsChange = (): void => {
setDelayResults(!delayResults);
};
Expand All @@ -111,9 +127,17 @@ export const PeoplePickerNormalExample: React.FunctionComponent = () => {
setShowSecondaryText(!showSecondaryText);
};

const onGetErrorMessage = React.useCallback(
(items: IPersonaProps[]): string | JSX.Element | PromiseLike<string | JSX.Element> | undefined => {
return isPickerRequired && (items || []).length === 0 ? 'Please fill out this field.' : undefined;
},
[isPickerRequired],
);

return (
<div>
<NormalPeoplePicker
label={showPickerLabel ? pickerLabel : undefined}
// eslint-disable-next-line react/jsx-no-bind
onResolveSuggestions={onFilterChanged}
// eslint-disable-next-line react/jsx-no-bind
Expand All @@ -137,6 +161,8 @@ export const PeoplePickerNormalExample: React.FunctionComponent = () => {
onInputChange={onInputChange}
resolveDelay={300}
disabled={isPickerDisabled}
required={isPickerRequired}
onGetErrorMessage={onGetErrorMessage}
/>
<Checkbox
label="Disable People Picker"
Expand All @@ -159,6 +185,28 @@ export const PeoplePickerNormalExample: React.FunctionComponent = () => {
onChange={onToggleShowSecondaryText}
styles={checkboxStyles}
/>
<Checkbox
label="Required People Picker"
checked={isPickerRequired}
// eslint-disable-next-line react/jsx-no-bind
onChange={onRequiredButtonClick}
styles={checkboxStyles}
/>
<Checkbox
label="Show Label"
checked={showPickerLabel}
// eslint-disable-next-line react/jsx-no-bind
onChange={onShowLabelButtonClick}
styles={checkboxStyles}
/>
{showPickerLabel && (
<TextField
label={'People Picker Label'}
value={pickerLabel}
// eslint-disable-next-line react/jsx-no-bind
onChange={onPickerLabelChange}
/>
)}
</div>
);
};
Expand Down
28 changes: 26 additions & 2 deletions packages/react/src/components/pickers/BasePicker.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ import type { IStyle } from '../../Styling';

const GlobalClassNames = {
root: 'ms-BasePicker',
label: 'ms-BasePicker-label',
text: 'ms-BasePicker-text',
itemsWrapper: 'ms-BasePicker-itemsWrapper',
input: 'ms-BasePicker-input',
error: 'ms-BasePicker-error',
};

export function getStyles(props: IBasePickerStyleProps): IBasePickerStyles {
const { className, theme, isFocused, inputClassName, disabled } = props;
const { className, theme, isFocused, inputClassName, disabled, hasErrorMessage } = props;

if (!theme) {
throw new Error('theme is undefined or null in base BasePicker getStyles function.');
Expand Down Expand Up @@ -56,8 +58,22 @@ export function getStyles(props: IBasePickerStyleProps): IBasePickerStyles {
// const disabledOverlayColor = rgbColor ? `rgba(${rgbColor.r}, ${rgbColor.g}, ${rgbColor.b}, 0.29)` : 'transparent';
const disabledOverlayColor = 'rgba(218, 218, 218, 0.29)';

const focusColor = isFocused && !disabled && (hasErrorMessage ? semanticColors.errorText : inputFocusBorderAlt);

return {
root: [classNames.root, className, { position: 'relative' }],
error: [
classNames.error,
{
fontSize: 12,
fontWeight: 400,
color: semanticColors.errorText,
margin: 0,
paddingTop: 5,
display: hasErrorMessage ? 'flex' : 'none',
alignItems: 'center',
},
],
text: [
classNames.text,
{
Expand All @@ -79,7 +95,7 @@ export function getStyles(props: IBasePickerStyleProps): IBasePickerStyles {
},
},
},
isFocused && !disabled && getInputFocusStyle(inputFocusBorderAlt, effects.roundedCorner2),
focusColor && getInputFocusStyle(focusColor, effects.roundedCorner2),
disabled && {
borderColor: disabledOverlayColor,
selectors: {
Expand All @@ -102,6 +118,14 @@ export function getStyles(props: IBasePickerStyleProps): IBasePickerStyles {
},
},
},
hasErrorMessage && {
borderColor: semanticColors.errorText,
selectors: {
':hover': {
borderColor: semanticColors.errorText,
},
},
},
],
itemsWrapper: [
classNames.itemsWrapper,
Expand Down
Loading
Loading