• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

alma-oss / spirit-design-system / 29920932531

22 Jul 2026 12:38PM UTC coverage: 85.616% (-0.8%) from 86.381%
29920932531

Pull #2814

github

pavelklibani
fixup! feat(web-react): introduce `UNSTABLE_Combobox` component #DS-2304
Pull Request #2814: 🚧 [WIP] feat(web-react): introduce `UNSTABLE_Combobox` component #DS-2304

2869 of 3878 branches covered (73.98%)

Branch coverage included in aggregate %.

401 of 492 new or added lines in 16 files covered. (81.5%)

7738 of 8511 relevant lines covered (90.92%)

226.44 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

67.35
/packages/web-react/src/components/UNSTABLE_Combobox/useComboboxOptionLabels.ts
1
'use client';
2

3
import { type ReactNode, type RefObject, useCallback, useEffect, useMemo, useRef } from 'react';
174✔
4
import { collectComboboxOptionLabels, getOptionRowEl as getOptionRowElFromListbox, getRowLabel } from './utils';
174✔
5

6
export interface ComboboxSelectedItem {
7
  label: string;
8
  value: string;
9
}
10

11
export interface UseComboboxOptionLabelsProps {
12
  children?: ReactNode;
13
  listboxRef: RefObject<HTMLElement | null>;
14
  selectedKeys: string[];
15
}
16

17
export interface UseComboboxOptionLabelsReturn {
18
  getOptionLabel: (key: string) => string;
19
  getOptionRowEl: (optionId: string) => HTMLElement | null;
20
  selectedItems: ComboboxSelectedItem[];
21
}
22

23
/**
24
 * Resolve option labels from children / DOM with a cache for selected tags.
25
 *
26
 * @param props Hook configuration
27
 * @param props.children Combobox option children
28
 * @param props.listboxRef Listbox grid element
29
 * @param props.selectedKeys Selected option ids
30
 * @returns {{ getOptionLabel: (key: string) => string, getOptionRowEl: (optionId: string) => HTMLElement | null, selectedItems: ComboboxSelectedItem[] }}
31
 */
32
export const useComboboxOptionLabels = ({
174✔
33
  children,
34
  listboxRef,
35
  selectedKeys,
36
}: UseComboboxOptionLabelsProps): UseComboboxOptionLabelsReturn => {
37
  const labelCacheRef = useRef<Map<string, string>>(new Map());
51✔
38

39
  const getOptionRowEl = useCallback(
51✔
40
    (optionId: string): HTMLElement | null => getOptionRowElFromListbox(listboxRef.current, optionId),
3✔
41
    [listboxRef],
42
  );
43

44
  const optionLabelsFromChildren = useMemo(() => collectComboboxOptionLabels(children), [children]);
51✔
45

46
  useEffect(() => {
51✔
47
    Object.entries(optionLabelsFromChildren).forEach(([key, optionLabel]) => {
40✔
48
      if (optionLabel) {
77!
49
        labelCacheRef.current.set(key, optionLabel);
77✔
50
      }
51
    });
52
  }, [optionLabelsFromChildren]);
53

54
  const syncLabelCacheFromDom = useCallback(() => {
51✔
55
    const listbox = listboxRef.current;
41✔
56

57
    if (!listbox) {
41✔
58
      return;
1✔
59
    }
60

61
    listbox.querySelectorAll<HTMLElement>('[role="row"][id]').forEach((row) => {
40✔
62
      if (row.id) {
79!
63
        labelCacheRef.current.set(row.id, getRowLabel(row));
79✔
64
      }
65
    });
66
  }, [listboxRef]);
67

68
  useEffect(() => {
51✔
69
    syncLabelCacheFromDom();
41✔
70
  }, [children, selectedKeys, syncLabelCacheFromDom]);
71

72
  const getOptionLabel = useCallback(
51✔
73
    (key: string) => {
74
      const fromChildren = optionLabelsFromChildren[key];
8✔
75

76
      if (fromChildren) {
8!
77
        labelCacheRef.current.set(key, fromChildren);
8✔
78

79
        return fromChildren;
8✔
80
      }
81

NEW
82
      const cached = labelCacheRef.current.get(key);
×
83

NEW
84
      if (cached) {
×
NEW
85
        return cached;
×
86
      }
87

NEW
88
      const rowEl = getOptionRowEl(key);
×
89

NEW
90
      if (rowEl) {
×
NEW
91
        const labelText = getRowLabel(rowEl);
×
92

NEW
93
        labelCacheRef.current.set(key, labelText);
×
94

NEW
95
        return labelText;
×
96
      }
97

NEW
98
      return key;
×
99
    },
100
    [getOptionRowEl, optionLabelsFromChildren],
101
  );
102

103
  const selectedItems = useMemo(
51✔
104
    () => selectedKeys.map((key) => ({ value: key, label: getOptionLabel(key) })),
41✔
105
    [getOptionLabel, selectedKeys],
106
  );
107

108
  return {
51✔
109
    getOptionLabel,
110
    getOptionRowEl,
111
    selectedItems,
112
  };
113
};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc