feat(tabs): adds user-defined custom tabs#75
Merged
Conversation
Components 1-6 of custom tabs implementation: - CustomTabSchema + BUILTIN_TAB_IDS + isBuiltinTab in schemas.ts - Config CRUD: addCustomTab, updateCustomTab, removeCustomTab, reorderCustomTab - View state: customTabFilters, expandedRepos widened to z.record, dynamic helpers - TabBar: TabId=string, custom tab rendering, + button, edit pencil - DashboardPage: custom tab routing, exclusiveOwnership, visibleIssues/PRs/Runs - Shared filter predicates: isIssueVisible, isPrVisible, isRunVisible - FilterBar: hideOrgRepo prop for custom tab context - 1863 tests (88 new), typecheck clean
- Adds customTabId and filterPreset props to IssuesTab, PullRequestsTab, ActionsTab - activeFilters memo with merge chain: schema defaults → preset → stored - Dispatches filter read/write/reset to customTabFilters or tabFilters - Dynamic tabKey() replaces all expandedRepos dot-notation accesses - Updates createReorderHighlight and createFlashDetection signals - globalFilter bypassed (null) when custom tab active - showScopeFilter returns true for custom tabs - Resolves _self sentinel in user filter preset - DashboardPage passes customTabId and filterPreset to custom tab renders
- CustomTabModal: Kobalte Dialog for create/edit custom tabs - Name, baseType, org/repo scope, filter presets, exclusive toggle - Filter preset fields derived from shared FilterChipGroupDef arrays - Extracts filter group constants to filterTypes.ts (single source) - Wires modal into DashboardPage replacing placeholder - _self sentinel in user filter preset for authenticated user
- CustomTabsSection: table with name, type badge, scope, exclusive, edit/delete/reorder actions - Integrates into SettingsPage after Tabs section - Updates handleExportSettings to include customTabs - Updates defaultTab dropdown to include custom tab options - Delete with window.confirm, disabled add at 10-tab cap
- Adds createEffect to reset all form signals when modal opens with a different editingTab (prevents stale state between edits) - Removes unnecessary orgList memo - Fixes resetCustomTabFilters import source
Security (SEC-001, SEC-002, SEC-006, SEC-008): - Constrains tab ID to alphanumeric/dash/underscore regex - Constrains orgScope entries to REPO_SEGMENT regex - Caps orgScope and repoScope at 100 entries - Prevents ID mutation via updateCustomTab Object.assign Accessibility (UI-002, UI-003, UI-005): - Associates name input and type select with labels (for/id) - Adds aria-expanded and aria-controls to scope collapsible - Adds aria-label to filter preset select dropdowns
- README: adds Custom Tabs section to feature list - USER_GUIDE: adds Custom Tabs subsection with creation, scope, filter presets, exclusivity, and management details
- CustomTabModal: 31 tests covering create/edit mode, validation, base type switching, scope selection, filter presets, exclusive toggle - CustomTabsSection: 27 tests covering table rendering, delete with confirm, reorder, empty state, cap enforcement, edit/add buttons - Total: 1927 tests (152 new from baseline)
Investigated Kobalte Tabs source: keyboard navigation uses
Collection-based delegates and querySelector('[data-key=...]')
for focus management — neither depend on direct children of
Tabs.List. Wrapper divs around custom tab triggers are safe.
Updated comment to reflect verified finding.
- Exclusive issues tab removes claimed items from built-in badge - Exclusive tab shows claimed items in its own badge count - Non-exclusive tabs do not affect built-in tab counts - First exclusive tab wins on overlapping scope - Type isolation: exclusive issues tab does not affect PRs/Actions - 1933 tests total (158 new from baseline)
- PERF-003: hoists Zod filter defaults to module-level constants
(avoids IssueFiltersSchema.parse({}) on every memo invocation)
- Adversarial-3: customTabData only filters the relevant baseType
per tab (was filtering all 3 types unconditionally)
- UI-007: empty state text no longer references hidden + button
- CR-014: createEffect closes modal when editingTabId references a deleted tab (prevents stale edit form) - UI-004: removes role='switch' from exclusive toggle (redundant on native checkbox), adds aria-describedby for description text - Updates test to query by id instead of removed role
- Delete button now shows inline 'Delete? [Yes] [No]' confirmation replacing the action buttons in the table row - Removes window.confirm dependency (blocks main thread, poor a11y, inconsistent with Kobalte Dialog pattern used elsewhere) - Updates tests to verify inline confirmation flow
- Correctness: case-insensitive repoScope matching, stale defaultTab cleanup in loadConfig, tabCounts badge applies filterPreset - Security: updateCustomTab validates via CustomTabSchema.safeParse - Performance: single-pass ignoredItems, visible* short-circuit on empty ownership, customTabData skips non-exclusive inactive tabs, hoisted Zod defaults outside loop - Quality: extract createTabFilterHandlers, formatScopeSummary, module-level baseTypeLabel/baseTypeBadgeClass, typed filterGroupsByType, editingTab createMemo, removed triple blank line - Tests: 37 new tests covering scoping, stale tab fallback, runtime redirect, orphan cleanup, schema validation, customTabId filter presets, scope cascade, hideOrgRepo, badge count integration
- Add scope (involves_me), user (surfacedBy), and sizeCategory filters to tabCounts badge computation for full parity with tab components - Gate checkStatus filter on pr.enriched (matches PullRequestsTab) - Export KNOWN_CONCLUSIONS and KNOWN_EVENTS from filterTypes.ts to eliminate magic array duplication between ActionsTab and DashboardPage - Narrow baseTypeLabel/baseTypeBadgeClass parameter to union type - Add loadConfig stale-defaultTab cleanup tests
6c911b1 to
c7856da
Compare
- fix PersonalSummaryStrip receiving unfiltered data instead of visible* memos - fix duplicate scope option in CustomTabModal filter preset dropdown - fix removeCustomTab not resetting viewState.lastActiveTab - extract buildTabScopeMatcher to deduplicate scope logic in DashboardPage - extract mergeActiveFilters to deduplicate filter merge in 3 tab components - hoist Zod filter schema defaults to module scope in DashboardPage - simplify CustomTabModal handleSave guard and getPresetValue - fix TabBar bracket notation, section numbering, local type alias - add tests for formatScopeSummary, createTabFilterHandlers, mergeActiveFilters, matchesScope OR semantics, _self sentinel in tabCounts, SettingsPage dropdown
- tabCounts now uses mergeActiveFilters() instead of inline merge (dedup) - add enriched guard to PR role filter in tabCounts (prevents count mismatch during light-phase when reviewerLogins/assigneeLogins are empty) - restore 'All (default)' option for non-scope filter groups in CustomTabModal (fix regression from duplicate scope option removal) - add removeCustomTab lastActiveTab reset tests (matching + non-matching)
- wraps TabBar +/edit and CustomTabsSection action buttons in Tooltip (matches RepoLockControls pattern) - replaces distracting pencil character with subtle cog SVG - changes placeholder from 'My OSAC PRs' to 'Needs Review'
- up/down tooltips show 'Already at top/bottom' when disabled (matches RepoLockControls pattern) - TabBar edit tooltip simplified to 'Edit tab' - TabBar edit icon uses same pencil SVG as CustomTabsSection
- 'Edit' → 'Edit "Tab Name"', 'Delete' → 'Delete "Tab Name"' (matches TrackedUsersSection pattern)
Resolves conflicts in ActionsTab, IssuesTab, and PullRequestsTab by combining upstream empty-locked-repo-row feature (isEmpty guard, EmptyLockedRepoRow fallback, flattened Show structure) with custom-tabs dynamic tabKey() and activeFilters() accessors.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary