Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
…rary into ENG-2834_nav_update
  • Loading branch information
AndyEPhipps committed Dec 2, 2024
2 parents c8ae835 + 526f5d0 commit 7e9cc4f
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 16 deletions.
73 changes: 59 additions & 14 deletions src/components/Atoms/TextInputWithDropdown/TextInputWithDropdown.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useRef } from 'react';
import PropTypes from 'prop-types';

import Input from '../Input/Input';
Expand All @@ -7,7 +7,6 @@ import {
Container,
Dropdown,
DropdownList,
DropdownItem,
DropdownItemSelectable,
TextItalic
} from './TextInputWithDropdown.style';
Expand Down Expand Up @@ -37,16 +36,44 @@ const TextInputWithDropdown = React.forwardRef(
label,
dropdownInstruction = null,
className = '',
hideBorder = false,
...otherInputProps
},
ref
forwardedRef
) => {
const [activeOption, setActiveOption] = useState(-1);
const [forceClosed, setForceClosed] = useState(false);
const dropdownRef = useRef(null);
const containerRef = useRef(null);

useEffect(() => {
// reset if options change
const handleClickOutside = event => {
if (dropdownRef.current
&& !dropdownRef.current.contains(event.target)
&& !containerRef.current.contains(event.target)) {
setForceClosed(true);
}
};

// Only add the listeners if we have options showing
if (options.length > 0 && !forceClosed) {
['mousedown', 'touchstart'].forEach(event => document.addEventListener(event, handleClickOutside));
}

return () => {
['mousedown', 'touchstart'].forEach(event => document.removeEventListener(event, handleClickOutside));
};
}, [options.length, forceClosed, onChange]);

const closeDropdown = () => {
setForceClosed(true);
setActiveOption(-1);
};

// Reset forceClosed when options change
useEffect(() => {
setForceClosed(false);
setActiveOption(-1);
}, [options]);

const down = () => (activeOption < options.length - 1
Expand Down Expand Up @@ -89,38 +116,43 @@ const TextInputWithDropdown = React.forwardRef(
onSelect,
dropdownInstruction,
activeOption,
closeDropdown,
resetActiveOption: () => setActiveOption(-1)
};

return (
<Container
className={`TextInputWithDropdown ${className}`.trim()}
onKeyDown={navigateOptions}
ref={containerRef}
>
<Input
{...inputProps}
className="TextInputWithDropdown__input"
ref={ref}
ref={forwardedRef}
/>
{options.length > 0 && forceClosed === false && (
{options.length > 0 && !forceClosed && (
<Options
{...optionsProps}
className="TextInputWithDropdown__options"
ref={dropdownRef}
hideBorder={hideBorder}
/>
)}
</Container>
);
}
);

const Options = ({
const Options = React.forwardRef(({
options,
dropdownInstruction,
onSelect,
activeOption,
closeDropdown,
resetActiveOption,
...rest
}) => {
}, ref) => {
// Reset 'activeOption' when the list is unfocused.
const onBlur = e => {
const { target } = e;
Expand All @@ -136,16 +168,24 @@ const Options = ({
return (
<Dropdown {...rest}>
<DropdownList
ref={ref}
role="listbox"
onBlur={onBlur}
aria-activedescendant={
activeOption > -1 ? `option-${activeOption}` : undefined
}
>
{dropdownInstruction && (
<DropdownItem role="option">
<TextItalic>{dropdownInstruction}</TextItalic>
</DropdownItem>
<DropdownItemSelectable
id="dropdown-instruction"
role="option"
key="dropdown-instruction"
onClick={closeDropdown}
>
<TextItalic>
{dropdownInstruction}
</TextItalic>
</DropdownItemSelectable>
)}
{options.map((option, index) => (
<DropdownItemSelectable
Expand Down Expand Up @@ -173,7 +213,7 @@ const Options = ({
</DropdownList>
</Dropdown>
);
};
});

TextInputWithDropdown.propTypes = {
options: PropTypes.arrayOf(PropTypes.string).isRequired,
Expand All @@ -183,15 +223,20 @@ TextInputWithDropdown.propTypes = {
name: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
className: PropTypes.string,
dropdownInstruction: PropTypes.string
dropdownInstruction: PropTypes.string,
hideBorder: PropTypes.bool
};

Options.propTypes = {
options: PropTypes.arrayOf(PropTypes.string).isRequired,
onSelect: PropTypes.func.isRequired,
dropdownInstruction: PropTypes.string,
activeOption: PropTypes.number.isRequired,
resetActiveOption: PropTypes.func.isRequired
resetActiveOption: PropTypes.func.isRequired,
hideBorder: PropTypes.bool,
closeDropdown: PropTypes.func
};

TextInputWithDropdown.displayName = 'TextInputWithDropdown';

export default TextInputWithDropdown;
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const Dropdown = styled.div`
max-height: 300px;
overflow: auto;
background-color: ${({ theme }) => theme.color('white')};
border: 1px solid;
border: ${({ hideBorder }) => (hideBorder ? 'none' : '1px solid')};
margin-top: -1px;
width: 100%;
Expand Down
7 changes: 6 additions & 1 deletion src/components/Molecules/SchoolLookup/SchoolLookup.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const SchoolLookup = React.forwardRef(
dropdownInstruction = 'Please select a school from the list below',
notFoundMessage = "Sorry, we can't find this school",
onSelect,
hideBorder = false,
...rest
},
ref
Expand All @@ -39,6 +40,7 @@ const SchoolLookup = React.forwardRef(
placeholder,
notFoundMessage,
dropdownInstruction,
hideBorder,
...rest
};

Expand All @@ -52,7 +54,10 @@ SchoolLookup.propTypes = {
label: PropTypes.string,
placeholder: PropTypes.string,
dropdownInstruction: PropTypes.string,
notFoundMessage: PropTypes.string
notFoundMessage: PropTypes.string,
hideBorder: PropTypes.bool
};

SchoolLookup.displayName = 'SchoolLookup';

export default SchoolLookup;

0 comments on commit 7e9cc4f

Please sign in to comment.