({count, state, children})
| 545 | } |
| 546 | |
| 547 | const SubTree: React.FC<TreeViewSubTreeProps> = ({count, state, children}) => { |
| 548 | const {announceUpdate} = React.useContext(RootContext) |
| 549 | const {itemId, isExpanded, isSubTreeEmpty, setIsSubTreeEmpty} = React.useContext(ItemContext) |
| 550 | const loadingItemRef = React.useRef<HTMLElement>(null) |
| 551 | const ref = React.useRef<HTMLElement>(null) |
| 552 | const [loadingFocused, setLoadingFocused] = React.useState(false) |
| 553 | const previousState = usePreviousValue(state) |
| 554 | const {safeSetTimeout} = useSafeTimeout() |
| 555 | |
| 556 | React.useEffect(() => { |
| 557 | // If `state` is undefined, we're working in a synchronous context and need |
| 558 | // to detect if the sub-tree has content. If `state === 'done` then we're |
| 559 | // working in an asynchronous context and need to see if there is content |
| 560 | // that has been loaded in. |
| 561 | if (state === undefined || state === 'done') { |
| 562 | if (!isSubTreeEmpty && !children) { |
| 563 | setIsSubTreeEmpty(true) |
| 564 | } else if (isSubTreeEmpty && children) { |
| 565 | setIsSubTreeEmpty(false) |
| 566 | } |
| 567 | } |
| 568 | }, [state, isSubTreeEmpty, setIsSubTreeEmpty, children]) |
| 569 | |
| 570 | // Handle transition from loading to done state |
| 571 | React.useEffect(() => { |
| 572 | if (previousState === 'loading' && state === 'done') { |
| 573 | const parentElement = document.getElementById(itemId) |
| 574 | if (!parentElement) return |
| 575 | |
| 576 | // Announce update to screen readers |
| 577 | const parentName = getAccessibleName(parentElement) |
| 578 | |
| 579 | if (ref.current?.childElementCount) { |
| 580 | announceUpdate(`${parentName} content loaded`) |
| 581 | } else { |
| 582 | announceUpdate(`${parentName} is empty`) |
| 583 | } |
| 584 | |
| 585 | // Move focus to the first child if the loading indicator |
| 586 | // was focused when the async items finished loading |
| 587 | if (loadingFocused) { |
| 588 | const firstChild = getFirstChildElement(parentElement) |
| 589 | |
| 590 | if (firstChild) { |
| 591 | safeSetTimeout(() => { |
| 592 | firstChild.focus() |
| 593 | }) |
| 594 | } else { |
| 595 | safeSetTimeout(() => { |
| 596 | parentElement.focus() |
| 597 | }) |
| 598 | } |
| 599 | |
| 600 | setLoadingFocused(false) |
| 601 | } |
| 602 | } |
| 603 | }, [loadingFocused, previousState, state, itemId, announceUpdate, ref, safeSetTimeout]) |
| 604 |
nothing calls this directly
no test coverage detected