Skip to content
Closed
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
113 changes: 80 additions & 33 deletions src/Form/FormAutosuggest.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, {
useEffect, useState,
} from 'react';
import PropTypes from 'prop-types';
import { v4 as uuidv4 } from 'uuid';
import { useIntl } from 'react-intl';
import { KeyboardArrowUp, KeyboardArrowDown } from '../../icons';
import Icon from '../Icon';
Expand All @@ -13,17 +14,21 @@ import Spinner from '../Spinner';
import useArrowKeyNavigation from '../hooks/useArrowKeyNavigation';
import messages from './messages';

//this is the function that is being called in the mdx file
function FormAutosuggest({
children,
arrowKeyNavigationSelector,
ignoredArrowKeysNames,
screenReaderText,
value,
freeformValue,
isLoading,
errorMessageText,
errorMessageText, //create more, more specific error no selected value
errorNoMatchingText, // error no matching value
onChange,
onSelected,
helpMessage,
allowFreeFormInput,
...props
}) {
const intl = useIntl();
Expand All @@ -34,40 +39,46 @@ function FormAutosuggest({
const [isMenuClosed, setIsMenuClosed] = useState(true);
const [isActive, setIsActive] = useState(false);
const [state, setState] = useState({
displayValue: value || '',
displayValue: freeformValue || '',
errorMessage: '',
dropDownItems: [],
});
console.log("VALUE:", value)

const handleItemClick = (e, onClick) => {
const clickedValue = e.currentTarget.getAttribute('data-value');

if (onSelected && clickedValue !== value) {
onSelected(clickedValue);
const clickedDataValue = e.currentTarget.getAttribute('data-value');
const clickedDisplayValue = e.currentTarget.innerText

if (onSelected && clickedDataValue !== value) {
onSelected({dataValue: clickedDataValue, displayValue: clickedDisplayValue, });
// call on selected and passes the object as an argument
// doc site example, selected will now be a data object
//then passes that in and passes that in as the value of the autosuggest component
// component believes its jsut eh display value, so whereever its value it should be displayvalue
}

setState(prevState => ({
...prevState,
dropDownItems: [],
displayValue: clickedValue,
displayValue: clickedDisplayValue,
}));

setIsMenuClosed(true);

if (onClick) {
onClick(e);
}
};
}
};

function getItems(strToFind = '') {
let childrenOpt = React.Children.map(children, (child) => {
// eslint-disable-next-line no-shadow
const { children, onClick, ...rest } = child.props;
const { children, onClick, value, ...rest } = child.props;

return React.cloneElement(child, {
...rest,
children,
'data-value': children,
'data-value': value ?? children,
onClick: (e) => handleItemClick(e, onClick),
});
});
Expand Down Expand Up @@ -103,6 +114,7 @@ function FormAutosuggest({
<IconButton
className="pgn__form-autosuggest__icon-button"
data-testid="autosuggest-iconbutton"
tabindex="-1"
src={isMenuClosed ? KeyboardArrowDown : KeyboardArrowUp}
iconAs={Icon}
size="sm"
Expand All @@ -113,18 +125,48 @@ function FormAutosuggest({
onClick={(e) => handleExpand(e, isMenuClosed)}
/>
);

const handleDocumentClick = (e) => {
if (parentRef.current && !parentRef.current.contains(e.target) && isActive) {
const leaveControl = () => {
setIsActive(false);

let errorMessage = ""

if (!state.displayValue){
console.log("Nothing typed:", errorMessageText)
errorMessage = errorMessageText
}else if(!allowFreeFormInput){ //not allowing for freeform, meaning must exactly match
console.log("Freeform not allowed:", errorNoMatchingText)
const dropDownItems = getItems(state.displayValue)
let inputMatchesDropDown = false
React.Children.forEach(children, (child) => {
console.log("item", child.props.children)
console.log(child.props)
if (state.displayValue === child.props.children){
onSelected({displayValue: state.displayValue, dataValue: child.props.value}) // freeform input is not allowed, HAS to match option values

inputMatchesDropDown = true
}
})
if (!inputMatchesDropDown){
onSelected({displayValue: "" , dataValue: ""}) //setting components parent value back to default
errorMessage = errorNoMatchingText
console.log("Freeform not allowed and doesn't match:", state.displayValue, dropDownItems, errorMessage)
}
}else{
onSelected({displayValue: state.displayValue, dataValue: state.displayValue}) // freeform IS allowed, therefore we are assigning the parent component value to be what the user types if there's no matching option
}

setState(prevState => ({
...prevState,
dropDownItems: [],
errorMessage: !state.displayValue ? errorMessageText : '',
errorMessage: errorMessage,
}));

setIsMenuClosed(true);
}

const handleDocumentClick = (e) => {
if (parentRef.current && !parentRef.current.contains(e.target) && isActive) {
leaveControl();
}
};

Expand All @@ -139,6 +181,9 @@ function FormAutosuggest({

setIsMenuClosed(true);
}
if (e.key === 'Tab' && isActive){
leaveControl();
}
};

useEffect(() => {
Expand All @@ -151,28 +196,29 @@ function FormAutosuggest({
};
});

useEffect(() => {
if (value || value === '') {
setState(prevState => ({
...prevState,
displayValue: value,
}));
}
}, [value]);
// Not clear what this was used for
// useEffect(() => {
// if (value || value === '') {
// setState(prevState => ({
// ...prevState,
// displayValue: freeformValue,
// }));
// }
// }, [value]);

const setDisplayValue = (itemValue) => {
const optValue = [];
const setDisplayValue = (typedValue) => {
const optValues = [];

children.forEach(opt => {
optValue.push(opt.props.children);
optValues.push({dataValue: opt.props.value, displayValue: opt.props.children, });
});

const normalized = itemValue.toLowerCase();
const opt = optValue.find((o) => o.toLowerCase() === normalized);
const normalized = typedValue.toLowerCase();
const opt = optValues.find((o) => o.displayValue.toLowerCase() === normalized);

setState(prevState => ({
...prevState,
displayValue: opt || itemValue,
displayValue: opt ? opt.displayValue : typedValue,
}));
};

Expand All @@ -193,9 +239,9 @@ function FormAutosuggest({

const handleOnChange = (e) => {
const findStr = e.target.value;

if (onChange) { onChange(findStr); }

if (findStr.length) {
const filteredItems = getItems(findStr);
setState(prevState => ({
Expand Down Expand Up @@ -279,7 +325,8 @@ FormAutosuggest.defaultProps = {
helpMessage: '',
placeholder: '',
value: null,
errorMessageText: null,
errorMessageText: "Error, no selected value",
errorNoMatchingText: "Error, no matching value",
readOnly: false,
children: null,
name: 'form-autosuggest',
Expand Down
26 changes: 17 additions & 9 deletions src/Form/form-autosuggest.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,31 @@ Form auto-suggest enables users to manually select or type to find matching opti

```jsx live
() => {
const [selected, setSelected] = useState('');
const [selectedDisplay, setSelectedDisplay] = useState('');
const [selectedData, setSelectedData] = useState('');
const handleSelected = (selectedObject) => {
setSelectedDisplay(selectedObject.displayValue)
setSelectedData(selectedObject.dataValue)
}

console.log("SELECTEDDISPLAY:", selectedDisplay)
console.log("SELECTEDDATA:", selectedData)

return (
<Form.Autosuggest
floatingLabel="Programming language"
aria-label="form autosuggest"
helpMessage="Select language"
errorMessageText="Error, no selected value"
value={selected}
onSelected={(value) => setSelected(value)}
errorNoMatchingText="Error, no matching value"
value={selectedData}
freeformValue={selectedDisplay}
onSelected={(value) => handleSelected(value)}
allowFreeFormInput={true}
>
<Form.AutosuggestOption>JavaScript</Form.AutosuggestOption>
<Form.AutosuggestOption>Python</Form.AutosuggestOption>
<Form.AutosuggestOption>Rube</Form.AutosuggestOption>
<Form.AutosuggestOption onClick={(e) => alert(e.currentTarget.getAttribute('data-value'))}>
Option with custom onClick
</Form.AutosuggestOption>
<Form.AutosuggestOption value="js">JavaScript</Form.AutosuggestOption>
<Form.AutosuggestOption value="py">Python</Form.AutosuggestOption>
<Form.AutosuggestOption value="rb">Ruby</Form.AutosuggestOption>
</Form.Autosuggest>
);
}
Expand Down