Skip to content

Commit

Permalink
feat: forward and back button for organize column search (#1641)
Browse files Browse the repository at this point in the history
Closes #1529
  • Loading branch information
ethanalvizo authored Nov 27, 2023
1 parent 90b7517 commit 89f2be5
Show file tree
Hide file tree
Showing 8 changed files with 343 additions and 24 deletions.
29 changes: 21 additions & 8 deletions packages/components/src/SearchInput.scss
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,30 @@
}
}

.search-match {
pointer-events: none;
.search-change-selection {
position: absolute;
right: $spacer-5;
right: $spacer-1;
top: 15%;
bottom: 15%;
height: 70%;
display: flex;
align-items: center;
padding: 0 $spacer-2;
border-radius: 1rem;
background-color: rgba($white, 0.25);
}

.search-change-button {
background: none;
border: none;
padding: 1px 2px;
}

.search-change-text {
background-color: rgba($white, 0.2);
border-radius: 10px;
padding: 1px 5px;
}

.search-match {
background-color: rgba($white, 0.2);
border-radius: 10px;
padding: 1px 5px;
margin: 0 5px;
}
}
101 changes: 94 additions & 7 deletions packages/components/src/SearchInput.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import React, { PureComponent } from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { vsSearch } from '@deephaven/icons';
import { vsArrowLeft, vsArrowRight, vsSearch } from '@deephaven/icons';
import classNames from 'classnames';
import Button from './Button';
import './SearchInput.scss';
import { GLOBAL_SHORTCUTS } from './shortcuts';
import { ContextActions } from './context-actions';

interface SearchInputProps {
value: string;
Expand All @@ -15,6 +18,10 @@ interface SearchInputProps {
matchCount: number;
id: string;
'data-testid'?: string;
cursor?: {
index: number | undefined;
next: (direction: 'forward' | 'back') => void;
};
}

class SearchInput extends PureComponent<SearchInputProps> {
Expand All @@ -27,19 +34,40 @@ class SearchInput extends PureComponent<SearchInputProps> {
},
id: '',
'data-testid': undefined,
cursor: undefined,
};

constructor(props: SearchInputProps) {
super(props);
this.inputField = React.createRef();
this.searchChangeSelection = React.createRef();
}

inputField: React.RefObject<HTMLInputElement>;
componentDidMount(): void {
this.setInputPaddingRight();
}

componentDidUpdate(): void {
this.setInputPaddingRight();
}

focus(): void {
this.inputField.current?.focus();
}

inputField: React.RefObject<HTMLInputElement>;

searchChangeSelection: React.RefObject<HTMLDivElement>;

setInputPaddingRight(): void {
const inputField = this.inputField.current;
const searchChangeSelection = this.searchChangeSelection.current;
if (inputField && searchChangeSelection) {
const paddingRight = searchChangeSelection.getBoundingClientRect().width;
inputField.style.paddingRight = `${paddingRight}px`;
}
}

render(): JSX.Element {
const {
value,
Expand All @@ -52,7 +80,56 @@ class SearchInput extends PureComponent<SearchInputProps> {
id,
onKeyDown,
'data-testid': dataTestId,
cursor,
} = this.props;

let matchCountSection;
const contextActions = [
{
action: () => cursor?.next('forward'),
shortcut: GLOBAL_SHORTCUTS.NEXT,
},
{
action: () => cursor?.next('back'),
shortcut: GLOBAL_SHORTCUTS.PREVIOUS,
},
];

if (cursor && matchCount > 1) {
matchCountSection = (
<>
<Button
kind="ghost"
className="search-change-button"
type="button"
onClick={() => {
cursor.next('back');
}}
icon={vsArrowLeft}
tooltip={`Previous match (${GLOBAL_SHORTCUTS.PREVIOUS.getDisplayText()})`}
/>
<span className="search-change-text">
{cursor.index !== undefined && `${cursor.index + 1} of `}
{matchCount}
</span>
<Button
kind="ghost"
className="search-change-button"
type="button"
onClick={() => {
cursor.next('forward');
}}
icon={vsArrowRight}
tooltip={`Next match (${GLOBAL_SHORTCUTS.NEXT.getDisplayText()})`}
/>
</>
);
} else {
matchCountSection = matchCount > 0 && (
<span className="search-match">{matchCount}</span>
);
}

return (
<div className={classNames('search-group', className)}>
<input
Expand All @@ -68,12 +145,22 @@ class SearchInput extends PureComponent<SearchInputProps> {
id={id}
data-testid={dataTestId}
/>
{matchCount != null && (
<span className="search-match">{matchCount}</span>

{matchCount != null ? (
<>
<div
className="search-change-selection"
ref={this.searchChangeSelection}
>
{matchCountSection}
</div>
<ContextActions actions={contextActions} />
</>
) : (
<span className="search-icon">
<FontAwesomeIcon icon={vsSearch} />
</span>
)}
<span className="search-icon">
<FontAwesomeIcon icon={vsSearch} />
</span>
</div>
);
}
Expand Down
14 changes: 14 additions & 0 deletions packages/components/src/shortcuts/GlobalShortcuts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,20 @@ const GLOBAL_SHORTCUTS = {
macShortcut: [KEY.ESCAPE],
isEditable: false,
}),
NEXT: ShortcutRegistry.createAndAdd({
id: 'GLOBAL.NEXT',
name: 'Next',
shortcut: [KEY.ENTER],
macShortcut: [KEY.ENTER],
isEditable: false,
}),
PREVIOUS: ShortcutRegistry.createAndAdd({
id: 'GLOBAL.PREVIOUS',
name: 'Previous',
shortcut: [MODIFIER.SHIFT, KEY.ENTER],
macShortcut: [MODIFIER.SHIFT, KEY.ENTER],
isEditable: false,
}),
};

export default GLOBAL_SHORTCUTS;
Original file line number Diff line number Diff line change
Expand Up @@ -1184,3 +1184,31 @@ test('On drag start/end', () => {
expect(mockGroupHandler).toBeCalledWith([]);
expect(mockMoveHandler).toBeCalledWith([{ from: 0, to: 1 }]);
});

test('changeSelectedColumn moves queried column index and loops', () => {
const builder = React.createRef<VisibilityOrderingBuilder>();
render(<Builder builderRef={builder} />);

builder.current?.searchColumns('TestColumn');
expect(builder.current?.state.selectedColumns.size).toEqual(10);

builder.current?.changeSelectedColumn('back');
expect(builder.current?.state.queriedColumnIndex).toEqual(9);

builder.current?.changeSelectedColumn('forward');
expect(builder.current?.state.queriedColumnIndex).toEqual(0);
});

test('adjustQueriedIndex sets queriedColumnRange to prevIndex = 9 and nextIndex = 0', () => {
const builder = React.createRef<VisibilityOrderingBuilder>();
render(<Builder builderRef={builder} />);

builder.current?.searchColumns('TestColumn');

builder.current?.adjustQueriedIndex('Test');

expect(builder.current?.state.queriedColumnRange).toEqual({
prevIndex: 9,
nextIndex: 0,
});
});
Loading

0 comments on commit 89f2be5

Please sign in to comment.