Skip to content
This repository has been archived by the owner on Oct 20, 2023. It is now read-only.

Commit

Permalink
Merge pull request #43 from cisagov/feature/command-navigation-expand…
Browse files Browse the repository at this point in the history
…ing-and-collapsing-command-rows
  • Loading branch information
GoldingAustin authored Nov 11, 2022
2 parents e20feb8 + 654ed0e commit 4c1cf6f
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 32 deletions.
26 changes: 18 additions & 8 deletions applications/client/src/store/graphql/BeaconModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { routes } from '@redeye/client/store';
import { TimeStatus } from '@redeye/client/types/timeline';
import { computed } from 'mobx';
import { ExtendedModel, getRoot, model, modelAction } from 'mobx-keystone';
import type { UUID } from '../../types';
import type { CurrentItem, UUID } from '../../types';
import { CampaignViews, Tabs } from '../../types';
import { BeaconModelBase } from './BeaconModel.base';
import type { OperatorModel } from './OperatorModel';
Expand Down Expand Up @@ -66,19 +66,29 @@ export class BeaconModel extends ExtendedModel(BeaconModelBase, {}) {
return operators;
}

@modelAction select() {
@modelAction select(activeItem?: CurrentItem, activeItemId?: UUID) {
const appStore = getRoot<AppStore>(this);
const notPrimary =
this.id !== appStore.campaign?.interactionState.selectedBeacon?.id ||
appStore.router.params.view !== CampaignViews.EXPLORE;
appStore.router.updateRoute({
path: routes[CampaignViews.EXPLORE],
params: {
view: CampaignViews.EXPLORE,
tab: notPrimary ? Tabs.COMMANDS : Tabs.BEACONS,
currentItem: notPrimary ? 'beacon' : 'all',
currentItemId: notPrimary ? (this.id as UUID) : undefined,
},
params:
activeItem && activeItemId
? {
view: CampaignViews.EXPLORE,
tab: notPrimary ? Tabs.COMMANDS : Tabs.BEACONS,
currentItem: notPrimary ? 'beacon' : 'all',
currentItemId: notPrimary ? (this.id as UUID) : undefined,
activeItem: notPrimary ? activeItem : undefined,
activeItemId: notPrimary ? (activeItemId as UUID) : undefined,
}
: {
view: CampaignViews.EXPLORE,
tab: notPrimary ? Tabs.COMMANDS : Tabs.BEACONS,
currentItem: notPrimary ? 'beacon' : 'all',
currentItemId: notPrimary ? (this.id as UUID) : undefined,
},
});
}

Expand Down
1 change: 1 addition & 0 deletions applications/client/src/views/Campaign/Explore/Explore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ export const Explore = observer<InfoProps>(({ ...props }) => {
isAscending={store.campaign.sort.direction === SortDirection.ASC}
toggleIsAscending={() => store.campaign.toggleIsAscending()}
filter={state.filter}
isCollapsible={store.router?.params.tab === Tabs.COMMANDS}
/>
{store.router?.params.tab === Tabs.COMMANDS &&
state.infoPanelType !== InfoType.OVERVIEW &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ type CommandContainerProps = ComponentProps<'div'> & {
setCommand?: (cmd: any) => any;
hideCommentButton?: boolean;
showPath?: boolean;
expandedCommandIDs?: string[];
removeExplandedCommandID?: (commandId: string) => void;
};

export const CommandContainer = observer<CommandContainerProps>(
Expand All @@ -32,30 +34,48 @@ export const CommandContainer = observer<CommandContainerProps>(
setCommand,
hideCommentButton = false,
showPath = false,
expandedCommandIDs = [],
removeExplandedCommandID,
...props
}) => {
const store = useStore();
const state = createState({
get active() {
return store.router.params.activeItem === 'command' && store.router.params.activeItemId === state.commandId;
},
get expanded() {
return store.router.params.activeItem === 'command' && expandedCommandIDs.includes(state.commandId);
},
setCollapsed() {
store.router.updateRoute({
path: store.router.currentRoute,
params: {
activeItem: state.active ? undefined : 'command',
activeItemId: state.active ? undefined : state.commandId,
},
});
if (!state.expanded) {
expandedCommandIDs.push(state.commandId);
store.router.updateRoute({
path: store.router.currentRoute,
params: {
activeItem: 'command',
activeItemId: state.commandId,
},
});
} else if (expandedCommandIDs?.length >= 1) {
if (expandedCommandIDs[expandedCommandIDs.length - 1] === state.commandId) {
store.router.updateRoute({
path: store.router.currentRoute,
params: {
activeItem: expandedCommandIDs.length > 1 ? 'command' : undefined,
activeItemId:
expandedCommandIDs.length > 1 ? (expandedCommandIDs[expandedCommandIDs.length - 2] as UUID) : undefined,
},
});
}
removeExplandedCommandID?.(state.commandId);
}
},
localCommand: undefined as undefined | CommandModel,
get commandId(): UUID | undefined {
return (command?.id ?? commandId!) as UUID;
},
get command(): CommandModel | undefined {
return state.commandId || command?.id
? store.graphqlStore.commands.get((state.commandId || command?.id)!)
: undefined;
return state.commandId ? store.graphqlStore.commands.get(state.commandId!) : undefined;
},
get skeletonClass() {
return state?.command?.inputText ? undefined : Classes.SKELETON;
Expand Down Expand Up @@ -83,7 +103,7 @@ export const CommandContainer = observer<CommandContainerProps>(
interactiveRowStyle,
gridFillStyle,
{ height: initialCommandRowHeight },
state.active ? activeCommandInfoRowStyle : undefined,
state.expanded || state.active ? activeCommandInfoRowStyle : undefined,
]}
onClick={state.setCollapsed}
onMouseEnter={() => store.campaign?.interactionState.onHover(state.command?.beacon?.current?.hierarchy || {})}
Expand All @@ -93,7 +113,7 @@ export const CommandContainer = observer<CommandContainerProps>(
store={store}
commandId={state.commandId}
skeletonClass={state.skeletonClass}
collapsed={!state.active}
collapsed={!state.expanded}
className={state.skeletonClass}
command={state.command}
showPath={showPath}
Expand All @@ -115,7 +135,7 @@ export const CommandContainer = observer<CommandContainerProps>(
/>
)}
</div>
{state.active && <CommandOutput command={state.command} />}
{(state.expanded || state.active) && <CommandOutput command={state.command} />}
</div>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { observer } from 'mobx-react-lite';
import type { ComponentProps } from 'react';
import { useEffect, useRef } from 'react';
import type { VirtuosoHandle } from 'react-virtuoso';
import { observable } from 'mobx';

type CommandsProps = ComponentProps<'div'> & {
showPath?: boolean;
Expand All @@ -23,6 +24,12 @@ export const Commands = observer<CommandsProps>(({ sort, showPath = true }) => {
startIndex: 0,
endIndex: 0,
},
expandedCommandIDs: store.router.params.activeItemId
? observable.array([store.router.params.activeItemId])
: observable.array<string>([]),
removeExplandedCommandID(commandId: string) {
this.expandedCommandIDs.remove(commandId);
},
scrollToCommand(commandId: string, commandIds: string[], behavior: ScrollBehavior = 'smooth') {
const commandIndex = commandIds.findIndex((id) => commandId === id);
if (commandIndex > -1) {
Expand Down Expand Up @@ -94,6 +101,12 @@ export const Commands = observer<CommandsProps>(({ sort, showPath = true }) => {
}
}, [store.campaign.commentStore.commentsOpen]);

useEffect(() => {
if (store.router.params.activeItem !== 'command') {
state.expandedCommandIDs.clear();
}
}, [store.router.params.activeItem]);

return (
<VirtualizedList
rangeChanged={(visibleRange) => state.update('visibleRange', visibleRange)}
Expand All @@ -106,7 +119,14 @@ export const Commands = observer<CommandsProps>(({ sort, showPath = true }) => {
<MessageRow>No Commands</MessageRow>
) : (
data?.commandIds?.map((commandId) => (
<CommandContainer commandId={commandId} key={commandId} data-command-id={commandId} showPath={showPath} />
<CommandContainer
commandId={commandId}
key={commandId}
data-command-id={commandId}
showPath={showPath}
expandedCommandIDs={state.expandedCommandIDs}
removeExplandedCommandID={state.removeExplandedCommandID}
/>
))
)}
</VirtualizedList>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Alignment, Button, Intent, MenuItem } from '@blueprintjs/core';
import type { ItemRenderer } from '@blueprintjs/select';
import { CaretDown16, CaretUp16, ChevronSort16, Minimize16 } from '@carbon/icons-react';
import { CaretDown16, CaretUp16, CollapseCategories16 } from '@carbon/icons-react';
import { css } from '@emotion/react';
import type { DropdownItem } from '@redeye/client/components';
import { CarbonIcon, createSorter, customIconPaths, Dropdown } from '@redeye/client/components';
Expand All @@ -11,7 +11,6 @@ import { sortOptions } from '@redeye/client/views';
import { FlexSplitter, Tokens } from '@redeye/ui-styles';
import { observer } from 'mobx-react-lite';
import type { ComponentProps } from 'react';
import { useState } from 'react';

type ControlBarProps = ComponentProps<'div'> & {
type: Tabs;
Expand Down Expand Up @@ -45,8 +44,6 @@ const renderSort: ItemRenderer<{ key: string; label: string }> = (item, { handle
export const ControlBar = observer<ControlBarProps>(
({ type, sortBy, setSortBy, filter, isCollapsible = false, isAscending, toggleIsAscending, ...props }) => {
const store = useStore();
// TODO: add state for Select.activeItem(s) and sort order?
const [expandAll, setExpandAll] = useState(true);

return (
<div
Expand Down Expand Up @@ -80,14 +77,19 @@ export const ControlBar = observer<ControlBarProps>(
*/}
{isCollapsible && (
<Button
icon={<CarbonIcon icon={expandAll ? ChevronSort16 : Minimize16} />}
// TODO: replace Minimize16 with custom icon for CollapseAll
title={expandAll ? 'Expand All' : 'Collapse All'}
icon={<CarbonIcon icon={CollapseCategories16} />}
title="Collapse All"
onClick={() => {
window.console.log('TODO: expand or collapse all list items');
setExpandAll(!expandAll);
store.router.updateRoute({
path: store.router.currentRoute,
params: {
activeItem: undefined,
activeItemId: undefined,
},
});
}}
minimal
disabled={!store.router.params.activeItem}
/>
)}
<Dropdown
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { observer } from 'mobx-react-lite';
import type { MouseEvent } from 'react';
import { useEffect } from 'react';
import { CampaignViews, Tabs } from '../../../../types';
import type { UUID } from '../../../../types';

type NavBreadcrumbsProps = Omit<BreadcrumbsProps, 'items'> &
BreadcrumbsStyledProps & {
Expand Down Expand Up @@ -146,7 +147,7 @@ export const NavBreadcrumbs = observer<NavBreadcrumbsProps>(
onClick: async (e) => {
e.stopPropagation();
await onNavigate(e);
this.command?.beacon?.current?.select();
this.command?.beacon?.current?.select('command', this.command?.id as UUID);
},
},
{
Expand Down

0 comments on commit 4c1cf6f

Please sign in to comment.