Skip to content

Commit

Permalink
feat: hide unexported decls in console by default and provide a toggl…
Browse files Browse the repository at this point in the history
  • Loading branch information
deniseli authored and jonathanj-square committed Oct 30, 2024
1 parent 0fa4912 commit 3bebd24
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 11 deletions.
4 changes: 4 additions & 0 deletions frontend/console/e2e/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ export async function navigateToModule(page: Page, moduleName: string) {
export async function navigateToDecl(page: Page, moduleName: string, declName: string) {
await navigateToModule(page, moduleName)

// Some decls are hidden by default because they are not exported, so click
// the toggle to make them visible.
await page.locator('#hide-exported').click()

// Navigate to the decl page
await page.locator(`a#decl-${declName}`).click()
await expect(page).toHaveURL(new RegExp(`/modules/${moduleName}/verb/${declName}`))
Expand Down
44 changes: 33 additions & 11 deletions frontend/console/src/features/modules/ModulesTree.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ArrowRight01Icon, ArrowShrink02Icon, CircleArrowRight02Icon, FileExportIcon, PackageIcon } from 'hugeicons-react'
import { ArrowRight01Icon, ArrowShrink02Icon, CircleArrowRight02Icon, PackageIcon, Upload01Icon } from 'hugeicons-react'
import { useEffect, useMemo, useRef, useState } from 'react'
import { Link, useParams, useSearchParams } from 'react-router-dom'
import { Multiselect, sortMultiselectOpts } from '../../components/Multiselect'
Expand All @@ -12,17 +12,14 @@ import {
declIcon,
declSumTypeIsExported,
declUrlFromInfo,
getHideUnexportedFromLocalStorage,
hasHideUnexportedInLocalStorage,
listExpandedModulesFromLocalStorage,
setHideUnexportedFromLocalStorage,
toggleModuleExpansionInLocalStorage,
} from './module.utils'
import { declTypeMultiselectOpts } from './schema/schema.utils'

const ExportedIcon = () => (
<span className='w-4' title='Exported'>
<FileExportIcon className='size-4 text-indigo-500 -ml-1' />
</span>
)

const DeclNode = ({ decl, href, isSelected }: { decl: DeclInfo; href: string; isSelected: boolean }) => {
const declRef = useRef<HTMLDivElement>(null)

Expand All @@ -45,12 +42,12 @@ const DeclNode = ({ decl, href, isSelected }: { decl: DeclInfo; href: string; is
ref={declRef}
className={classNames(
isSelected ? 'bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 hover:dark:bg-gray-600' : 'hover:bg-gray-200 hover:dark:bg-gray-700',
declSumTypeIsExported(decl.value) ? '' : 'text-gray-400 dark:text-gray-500',
'group flex items-center gap-x-2 pl-4 pr-2 text-sm font-light leading-6 w-full cursor-pointer scroll-mt-10',
)}
>
<Icon aria-hidden='true' className='size-4 shrink-0 ml-3' />
{decl.value.name}
{declSumTypeIsExported(decl.value) ? <ExportedIcon /> : []}
</div>
</Link>
</li>
Expand All @@ -62,7 +59,8 @@ const ModuleSection = ({
isExpanded,
toggleExpansion,
selectedDeclTypes,
}: { module: ModuleTreeItem; isExpanded: boolean; toggleExpansion: (m: string) => void; selectedDeclTypes: MultiselectOpt[] }) => {
hideUnexported,
}: { module: ModuleTreeItem; isExpanded: boolean; toggleExpansion: (m: string) => void; selectedDeclTypes: MultiselectOpt[]; hideUnexported: boolean }) => {
const { moduleName, declName } = useParams()
const isSelected = useMemo(() => moduleName === module.name, [moduleName, module.name])
const moduleRef = useRef<HTMLDivElement>(null)
Expand All @@ -78,7 +76,13 @@ const ModuleSection = ({
}
}, [moduleName]) // moduleName is the selected module; module.name is the one being rendered

const filteredDecls = useMemo(() => module.decls.filter((d) => !!selectedDeclTypes.find((o) => o.key === d.declType)), [module.decls, selectedDeclTypes])
const filteredDecls = useMemo(
() =>
module.decls
.filter((d) => !!selectedDeclTypes.find((o) => o.key === d.declType))
.filter((d) => !hideUnexported || (isSelected && declName === d.value.name) || declSumTypeIsExported(d.value)),
[module.decls, selectedDeclTypes, hideUnexported],
)

return (
<li key={module.name} id={`module-tree-module-${module.name}`} className='mb-2'>
Expand Down Expand Up @@ -130,6 +134,8 @@ export const ModulesTree = ({ modules }: { modules: ModuleTreeItem[] }) => {
setExpandedModules(listExpandedModulesFromLocalStorage())
}, [moduleName, declName])

const [hideUnexported, setHideUnexported] = useState(hasHideUnexportedInLocalStorage() ? getHideUnexportedFromLocalStorage() : true)

function msOnChange(opts: MultiselectOpt[]) {
const params = new URLSearchParams()
if (opts.length !== declTypeMultiselectOpts.length) {
Expand All @@ -154,17 +160,32 @@ export const ModulesTree = ({ modules }: { modules: ModuleTreeItem[] }) => {
setExpandedModules(listExpandedModulesFromLocalStorage())
}

function setHideUnexportedState(val: boolean) {
setHideUnexportedFromLocalStorage(val)
setHideUnexported(val)
}

modules.sort((m1, m2) => Number(m1.isBuiltin) - Number(m2.isBuiltin))
return (
<div className='flex grow flex-col h-full gap-y-5 overflow-y-auto bg-gray-100 dark:bg-gray-900'>
<nav>
<div className='sticky top-0 border-b border-gray-300 bg-gray-100 dark:border-gray-800 dark:bg-gray-900 z-10'>
<span className='block w-[calc(100%-32px)]'>
<span className='block w-[calc(100%-62px)]'>
<Multiselect allOpts={declTypeMultiselectOpts} selectedOpts={selectedDeclTypes} onChange={msOnChange} />
</span>
<span
className='absolute inset-y-0 right-0 flex items-center px-1 mx-9 my-1.5 rounded-md cursor-pointer bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-600 hover:bg-gray-100 dark:hover:bg-gray-700 hover:text-gray-800 dark:hover:text-gray-100'
onClick={() => setHideUnexportedState(!hideUnexported)}
id='hide-exported'
title='show/hide unexported'
>
<Upload01Icon className={`size-5 ${hideUnexported ? 'text-gray-300 dark:text-gray-600' : 'text-gray-500 dark:text-gray-300'}`} />
{hideUnexported ? <div className='absolute border-t border-gray-300 dark:border-gray-600 rotate-45 w-9 -ml-2' /> : ''}
</span>
<span
className='absolute inset-y-0 right-0 flex items-center px-1 mx-1 my-1.5 rounded-md cursor-pointer bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-600 hover:bg-gray-100 dark:hover:bg-gray-700 hover:text-gray-800 dark:hover:text-gray-100'
onClick={collapseAll}
title='collapse all modules'
>
<ArrowShrink02Icon className='size-5 text-gray-500 dark:text-gray-300' />
</span>
Expand All @@ -177,6 +198,7 @@ export const ModulesTree = ({ modules }: { modules: ModuleTreeItem[] }) => {
isExpanded={expandedModules.includes(m.name)}
toggleExpansion={toggle}
selectedDeclTypes={selectedDeclTypes}
hideUnexported={hideUnexported}
/>
))}
</ul>
Expand Down
6 changes: 6 additions & 0 deletions frontend/console/src/features/modules/module.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,12 @@ export const declFromModules = (moduleName: string, declCase: string, declName:
}
}

export const hasHideUnexportedInLocalStorage = () => localStorage.getItem('tree_hu') !== null

export const getHideUnexportedFromLocalStorage = () => localStorage.getItem('tree_hu') === 'true'

export const setHideUnexportedFromLocalStorage = (val: boolean) => localStorage.setItem('tree_hu', val ? 'true' : 'false')

export const listExpandedModulesFromLocalStorage = () => (localStorage.getItem('tree_m') || '').split(',').filter((s) => s !== '')

export const toggleModuleExpansionInLocalStorage = (moduleName: string) => {
Expand Down

0 comments on commit 3bebd24

Please sign in to comment.