| 116 | * javascript. |
| 117 | */ |
| 118 | export default class Dropdown extends React.Component<DropdownProps, IState> { |
| 119 | private readonly buttonRef = createRef<HTMLDivElement>(); |
| 120 | private dropdownRootElement: HTMLDivElement | null = null; |
| 121 | private ignoreEvent: MouseEvent | null = null; |
| 122 | private childrenByKey: Record<string, ReactNode> = {}; |
| 123 | |
| 124 | public constructor(props: DropdownProps) { |
| 125 | super(props); |
| 126 | |
| 127 | this.reindexChildren(this.props.children); |
| 128 | |
| 129 | const firstChild = props.children[0]; |
| 130 | |
| 131 | this.state = { |
| 132 | // True if the menu is dropped-down |
| 133 | expanded: false, |
| 134 | // The key of the highlighted option |
| 135 | // (the option that would become selected if you pressed enter) |
| 136 | highlightedOption: firstChild.key, |
| 137 | // the current search query |
| 138 | searchQuery: "", |
| 139 | }; |
| 140 | } |
| 141 | |
| 142 | public componentDidMount(): void { |
| 143 | // Listen for all clicks on the document so we can close the |
| 144 | // menu when the user clicks somewhere else |
| 145 | document.addEventListener("click", this.onDocumentClick, false); |
| 146 | } |
| 147 | |
| 148 | public componentDidUpdate(prevProps: Readonly<DropdownProps>): void { |
| 149 | if (objectHasDiff(this.props, prevProps) && this.props.children?.length) { |
| 150 | this.reindexChildren(this.props.children); |
| 151 | const firstChild = this.props.children[0]; |
| 152 | this.setState({ |
| 153 | highlightedOption: firstChild.key, |
| 154 | }); |
| 155 | } |
| 156 | } |
| 157 | |
| 158 | public componentWillUnmount(): void { |
| 159 | document.removeEventListener("click", this.onDocumentClick, false); |
| 160 | } |
| 161 | |
| 162 | private reindexChildren(children: ReactElement[]): void { |
| 163 | this.childrenByKey = {}; |
| 164 | React.Children.forEach(children, (child) => { |
| 165 | this.childrenByKey[(child as DropdownProps["children"][number]).key] = child; |
| 166 | }); |
| 167 | } |
| 168 | |
| 169 | private onDocumentClick = (ev: MouseEvent): void => { |
| 170 | // Close the dropdown if the user clicks anywhere that isn't |
| 171 | // within our root element |
| 172 | if (ev !== this.ignoreEvent) { |
| 173 | this.setState({ |
| 174 | expanded: false, |
| 175 | }); |
nothing calls this directly
no test coverage detected