feat: US-006 - Frontend: Icon picker as compact dropdown
This commit is contained in:
@@ -1767,9 +1767,23 @@ def run_all_tests():
|
||||
test_css_transition_300ms,
|
||||
test_height_constraint_collapsed,
|
||||
test_typecheck_us005,
|
||||
# US-006 tests
|
||||
test_icon_picker_is_dropdown,
|
||||
test_icon_picker_has_search_input,
|
||||
test_icon_picker_max_height_300px,
|
||||
test_icon_picker_toggle_functions,
|
||||
test_icon_picker_visible_class_toggle,
|
||||
test_icon_picker_filter_function,
|
||||
test_icon_picker_closes_on_selection,
|
||||
test_icon_picker_click_outside_closes,
|
||||
test_icon_picker_updates_trigger_display,
|
||||
test_icon_picker_css_dropdown_styles,
|
||||
test_icon_picker_chevron_rotation,
|
||||
test_icon_picker_search_autofocus,
|
||||
test_typecheck_us006,
|
||||
]
|
||||
|
||||
print(f"\nRunning {len(tests)} frontend tests for US-002, US-003, US-005, US-006 through US-014...\n")
|
||||
print(f"\nRunning {len(tests)} frontend tests for US-002, US-003, US-004, US-005, US-006 through US-014...\n")
|
||||
|
||||
failed = []
|
||||
for test in tests:
|
||||
@@ -2530,3 +2544,190 @@ def test_typecheck_us005():
|
||||
|
||||
if __name__ == '__main__':
|
||||
run_all_tests()
|
||||
|
||||
# US-006: Frontend: Icon picker as compact dropdown
|
||||
def test_icon_picker_is_dropdown():
|
||||
"""Test 151: Icon picker renders as dropdown with trigger button"""
|
||||
habits_path = Path(__file__).parent.parent / 'habits.html'
|
||||
html = habits_path.read_text()
|
||||
|
||||
# Check for dropdown structure
|
||||
assert 'icon-picker-dropdown' in html, "Icon picker should be a dropdown"
|
||||
assert 'icon-picker-trigger' in html, "Dropdown should have trigger button"
|
||||
assert 'icon-picker-content' in html, "Dropdown should have content container"
|
||||
|
||||
# Check trigger button has icon display and chevron
|
||||
assert 'selectedIconDisplay' in html, "Trigger should display selected icon"
|
||||
assert 'iconPickerChevron' in html, "Trigger should have chevron icon"
|
||||
assert 'chevron-down' in html, "Chevron should be down arrow"
|
||||
|
||||
print("✓ Test 151 passed: Icon picker is dropdown structure")
|
||||
|
||||
def test_icon_picker_has_search_input():
|
||||
"""Test 152: Dropdown has search input for filtering icons"""
|
||||
habits_path = Path(__file__).parent.parent / 'habits.html'
|
||||
html = habits_path.read_text()
|
||||
|
||||
# Check for search input
|
||||
assert 'icon-search-input' in html, "Dropdown should have search input"
|
||||
assert 'id="iconSearch"' in html, "Search input should have iconSearch id"
|
||||
assert 'placeholder="Search icons..."' in html, "Search input should have placeholder"
|
||||
assert 'oninput="filterIcons()"' in html, "Search input should trigger filterIcons()"
|
||||
|
||||
print("✓ Test 152 passed: Dropdown has search input")
|
||||
|
||||
def test_icon_picker_max_height_300px():
|
||||
"""Test 153: Dropdown content has max-height 300px with scroll"""
|
||||
habits_path = Path(__file__).parent.parent / 'habits.html'
|
||||
html = habits_path.read_text()
|
||||
|
||||
# Check for max-height in CSS
|
||||
assert 'max-height: 300px' in html, "Icon picker content should have max-height 300px"
|
||||
assert 'overflow-y: auto' in html or 'overflow: hidden' in html, "Should have overflow handling"
|
||||
|
||||
print("✓ Test 153 passed: Max-height 300px with scroll")
|
||||
|
||||
def test_icon_picker_toggle_functions():
|
||||
"""Test 154: Toggle functions exist for opening/closing dropdown"""
|
||||
habits_path = Path(__file__).parent.parent / 'habits.html'
|
||||
html = habits_path.read_text()
|
||||
|
||||
# Check for toggle functions
|
||||
assert 'function toggleIconPicker()' in html, "toggleIconPicker function should exist"
|
||||
assert 'function openIconPicker()' in html, "openIconPicker function should exist"
|
||||
assert 'function closeIconPicker()' in html, "closeIconPicker function should exist"
|
||||
assert 'onclick="toggleIconPicker()"' in html, "Trigger should call toggleIconPicker"
|
||||
|
||||
print("✓ Test 154 passed: Toggle functions exist")
|
||||
|
||||
def test_icon_picker_visible_class_toggle():
|
||||
"""Test 155: Dropdown uses visible class to show/hide content"""
|
||||
habits_path = Path(__file__).parent.parent / 'habits.html'
|
||||
html = habits_path.read_text()
|
||||
|
||||
# Check for visible class handling in CSS
|
||||
assert '.icon-picker-content.visible' in html, "CSS should have visible class rule"
|
||||
assert 'display: block' in html, "Visible class should display content"
|
||||
assert 'display: none' in html, "Hidden state should have display none"
|
||||
|
||||
# Check for visible class toggling in JS
|
||||
assert "classList.add('visible')" in html, "Should add visible class to open"
|
||||
assert "classList.remove('visible')" in html, "Should remove visible class to close"
|
||||
|
||||
print("✓ Test 155 passed: Visible class toggle logic")
|
||||
|
||||
def test_icon_picker_filter_function():
|
||||
"""Test 156: filterIcons function filters icons based on search"""
|
||||
habits_path = Path(__file__).parent.parent / 'habits.html'
|
||||
html = habits_path.read_text()
|
||||
|
||||
# Check for filterIcons function
|
||||
assert 'function filterIcons()' in html, "filterIcons function should exist"
|
||||
|
||||
# Check filter logic
|
||||
search_section = html[html.find('function filterIcons()'):html.find('function filterIcons()') + 1000]
|
||||
assert 'toLowerCase()' in search_section, "Should use case-insensitive search"
|
||||
assert 'filter' in search_section, "Should use filter method"
|
||||
assert 'includes' in search_section, "Should check if icon name includes query"
|
||||
|
||||
print("✓ Test 156 passed: filterIcons function implementation")
|
||||
|
||||
def test_icon_picker_closes_on_selection():
|
||||
"""Test 157: Selecting an icon closes the dropdown"""
|
||||
habits_path = Path(__file__).parent.parent / 'habits.html'
|
||||
html = habits_path.read_text()
|
||||
|
||||
# Check that selectIcon calls closeIconPicker
|
||||
select_icon_section = html[html.find('function selectIcon('):html.find('function selectIcon(') + 1500]
|
||||
assert 'closeIconPicker()' in select_icon_section, "selectIcon should call closeIconPicker"
|
||||
|
||||
print("✓ Test 157 passed: Selection closes dropdown")
|
||||
|
||||
def test_icon_picker_click_outside_closes():
|
||||
"""Test 158: Clicking outside dropdown closes it"""
|
||||
habits_path = Path(__file__).parent.parent / 'habits.html'
|
||||
html = habits_path.read_text()
|
||||
|
||||
# Check for click outside listener comment and handler
|
||||
assert "Click outside icon picker to close" in html, "Should have icon picker click-outside comment"
|
||||
|
||||
# Find the icon picker click handler (after the comment)
|
||||
click_handler_start = html.find("Click outside icon picker to close")
|
||||
if click_handler_start > 0:
|
||||
click_handler = html[click_handler_start:click_handler_start + 500]
|
||||
assert 'icon-picker-dropdown' in click_handler, "Should reference icon-picker-dropdown"
|
||||
assert 'contains' in click_handler, "Should check if click is inside dropdown"
|
||||
assert 'closeIconPicker()' in click_handler, "Should close if clicked outside"
|
||||
|
||||
print("✓ Test 158 passed: Click outside closes dropdown")
|
||||
|
||||
def test_icon_picker_updates_trigger_display():
|
||||
"""Test 159: Selected icon updates trigger button display"""
|
||||
habits_path = Path(__file__).parent.parent / 'habits.html'
|
||||
html = habits_path.read_text()
|
||||
|
||||
# Check that selectIcon updates the trigger display
|
||||
select_icon_section = html[html.find('function selectIcon('):html.find('function selectIcon(') + 1500]
|
||||
assert 'selectedIconDisplay' in select_icon_section, "Should update selectedIconDisplay element"
|
||||
assert 'setAttribute' in select_icon_section, "Should set icon attribute"
|
||||
assert 'data-lucide' in select_icon_section, "Should update data-lucide attribute"
|
||||
assert 'lucide.createIcons()' in select_icon_section, "Should refresh Lucide icons"
|
||||
|
||||
print("✓ Test 159 passed: Trigger display updates on selection")
|
||||
|
||||
def test_icon_picker_css_dropdown_styles():
|
||||
"""Test 160: CSS includes dropdown-specific styles"""
|
||||
habits_path = Path(__file__).parent.parent / 'habits.html'
|
||||
html = habits_path.read_text()
|
||||
|
||||
# Check for dropdown-specific CSS
|
||||
assert '.icon-picker-dropdown' in html, "Should have dropdown container styles"
|
||||
assert '.icon-picker-trigger' in html, "Should have trigger button styles"
|
||||
assert '.icon-picker-content' in html, "Should have content container styles"
|
||||
assert 'position: absolute' in html, "Content should be absolutely positioned"
|
||||
assert 'z-index: 100' in html, "Content should have high z-index"
|
||||
|
||||
# Check for hover states
|
||||
assert '.icon-picker-trigger:hover' in html, "Should have trigger hover state"
|
||||
|
||||
print("✓ Test 160 passed: Dropdown CSS styles present")
|
||||
|
||||
def test_icon_picker_chevron_rotation():
|
||||
"""Test 161: Chevron rotates when dropdown opens"""
|
||||
habits_path = Path(__file__).parent.parent / 'habits.html'
|
||||
html = habits_path.read_text()
|
||||
|
||||
# Check for chevron rotation in CSS
|
||||
assert 'trigger-chevron' in html, "Chevron should have trigger-chevron class"
|
||||
assert 'transform: rotate(180deg)' in html, "Chevron should rotate 180deg when open"
|
||||
assert '.open .trigger-chevron' in html or '.icon-picker-trigger.open .trigger-chevron' in html, "Should rotate when trigger has open class"
|
||||
|
||||
# Check that open class is toggled
|
||||
assert "classList.add('open')" in html, "Should add open class"
|
||||
assert "classList.remove('open')" in html, "Should remove open class"
|
||||
|
||||
print("✓ Test 161 passed: Chevron rotation logic")
|
||||
|
||||
def test_icon_picker_search_autofocus():
|
||||
"""Test 162: Search input gets focus when dropdown opens"""
|
||||
habits_path = Path(__file__).parent.parent / 'habits.html'
|
||||
html = habits_path.read_text()
|
||||
|
||||
# Check that openIconPicker focuses the search input
|
||||
open_icon_picker = html[html.find('function openIconPicker()'):html.find('function openIconPicker()') + 800]
|
||||
assert 'focus()' in open_icon_picker, "Should focus search input when opening"
|
||||
assert 'setTimeout' in open_icon_picker, "Should use setTimeout for focus after animation"
|
||||
|
||||
print("✓ Test 162 passed: Search input autofocus")
|
||||
|
||||
def test_typecheck_us006():
|
||||
"""Test 163: Typecheck passes"""
|
||||
result = subprocess.run(
|
||||
['python3', '-m', 'py_compile', 'dashboard/api.py', 'dashboard/habits_helpers.py'],
|
||||
cwd='/home/moltbot/clawd',
|
||||
capture_output=True,
|
||||
text=True
|
||||
)
|
||||
assert result.returncode == 0, f"Typecheck failed: {result.stderr}"
|
||||
print("✓ Test 163 passed: Typecheck successful")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user