Skip to content

Commit 89f2be5

Browse files
authored
feat: forward and back button for organize column search (#1641)
Closes #1529
1 parent 90b7517 commit 89f2be5

8 files changed

Lines changed: 343 additions & 24 deletions

File tree

packages/components/src/SearchInput.scss

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,17 +42,30 @@
4242
}
4343
}
4444

45-
.search-match {
46-
pointer-events: none;
45+
.search-change-selection {
4746
position: absolute;
48-
right: $spacer-5;
47+
right: $spacer-1;
4948
top: 15%;
5049
bottom: 15%;
5150
height: 70%;
52-
display: flex;
53-
align-items: center;
54-
padding: 0 $spacer-2;
55-
border-radius: 1rem;
56-
background-color: rgba($white, 0.25);
51+
}
52+
53+
.search-change-button {
54+
background: none;
55+
border: none;
56+
padding: 1px 2px;
57+
}
58+
59+
.search-change-text {
60+
background-color: rgba($white, 0.2);
61+
border-radius: 10px;
62+
padding: 1px 5px;
63+
}
64+
65+
.search-match {
66+
background-color: rgba($white, 0.2);
67+
border-radius: 10px;
68+
padding: 1px 5px;
69+
margin: 0 5px;
5770
}
5871
}

packages/components/src/SearchInput.tsx

Lines changed: 94 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import React, { PureComponent } from 'react';
22
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
3-
import { vsSearch } from '@deephaven/icons';
3+
import { vsArrowLeft, vsArrowRight, vsSearch } from '@deephaven/icons';
44
import classNames from 'classnames';
5+
import Button from './Button';
56
import './SearchInput.scss';
7+
import { GLOBAL_SHORTCUTS } from './shortcuts';
8+
import { ContextActions } from './context-actions';
69

710
interface SearchInputProps {
811
value: string;
@@ -15,6 +18,10 @@ interface SearchInputProps {
1518
matchCount: number;
1619
id: string;
1720
'data-testid'?: string;
21+
cursor?: {
22+
index: number | undefined;
23+
next: (direction: 'forward' | 'back') => void;
24+
};
1825
}
1926

2027
class SearchInput extends PureComponent<SearchInputProps> {
@@ -27,19 +34,40 @@ class SearchInput extends PureComponent<SearchInputProps> {
2734
},
2835
id: '',
2936
'data-testid': undefined,
37+
cursor: undefined,
3038
};
3139

3240
constructor(props: SearchInputProps) {
3341
super(props);
3442
this.inputField = React.createRef();
43+
this.searchChangeSelection = React.createRef();
3544
}
3645

37-
inputField: React.RefObject<HTMLInputElement>;
46+
componentDidMount(): void {
47+
this.setInputPaddingRight();
48+
}
49+
50+
componentDidUpdate(): void {
51+
this.setInputPaddingRight();
52+
}
3853

3954
focus(): void {
4055
this.inputField.current?.focus();
4156
}
4257

58+
inputField: React.RefObject<HTMLInputElement>;
59+
60+
searchChangeSelection: React.RefObject<HTMLDivElement>;
61+
62+
setInputPaddingRight(): void {
63+
const inputField = this.inputField.current;
64+
const searchChangeSelection = this.searchChangeSelection.current;
65+
if (inputField && searchChangeSelection) {
66+
const paddingRight = searchChangeSelection.getBoundingClientRect().width;
67+
inputField.style.paddingRight = `${paddingRight}px`;
68+
}
69+
}
70+
4371
render(): JSX.Element {
4472
const {
4573
value,
@@ -52,7 +80,56 @@ class SearchInput extends PureComponent<SearchInputProps> {
5280
id,
5381
onKeyDown,
5482
'data-testid': dataTestId,
83+
cursor,
5584
} = this.props;
85+
86+
let matchCountSection;
87+
const contextActions = [
88+
{
89+
action: () => cursor?.next('forward'),
90+
shortcut: GLOBAL_SHORTCUTS.NEXT,
91+
},
92+
{
93+
action: () => cursor?.next('back'),
94+
shortcut: GLOBAL_SHORTCUTS.PREVIOUS,
95+
},
96+
];
97+
98+
if (cursor && matchCount > 1) {
99+
matchCountSection = (
100+
<>
101+
<Button
102+
kind="ghost"
103+
className="search-change-button"
104+
type="button"
105+
onClick={() => {
106+
cursor.next('back');
107+
}}
108+
icon={vsArrowLeft}
109+
tooltip={`Previous match (${GLOBAL_SHORTCUTS.PREVIOUS.getDisplayText()})`}
110+
/>
111+
<span className="search-change-text">
112+
{cursor.index !== undefined && `${cursor.index + 1} of `}
113+
{matchCount}
114+
</span>
115+
<Button
116+
kind="ghost"
117+
className="search-change-button"
118+
type="button"
119+
onClick={() => {
120+
cursor.next('forward');
121+
}}
122+
icon={vsArrowRight}
123+
tooltip={`Next match (${GLOBAL_SHORTCUTS.NEXT.getDisplayText()})`}
124+
/>
125+
</>
126+
);
127+
} else {
128+
matchCountSection = matchCount > 0 && (
129+
<span className="search-match">{matchCount}</span>
130+
);
131+
}
132+
56133
return (
57134
<div className={classNames('search-group', className)}>
58135
<input
@@ -68,12 +145,22 @@ class SearchInput extends PureComponent<SearchInputProps> {
68145
id={id}
69146
data-testid={dataTestId}
70147
/>
71-
{matchCount != null && (
72-
<span className="search-match">{matchCount}</span>
148+
149+
{matchCount != null ? (
150+
<>
151+
<div
152+
className="search-change-selection"
153+
ref={this.searchChangeSelection}
154+
>
155+
{matchCountSection}
156+
</div>
157+
<ContextActions actions={contextActions} />
158+
</>
159+
) : (
160+
<span className="search-icon">
161+
<FontAwesomeIcon icon={vsSearch} />
162+
</span>
73163
)}
74-
<span className="search-icon">
75-
<FontAwesomeIcon icon={vsSearch} />
76-
</span>
77164
</div>
78165
);
79166
}

packages/components/src/shortcuts/GlobalShortcuts.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,20 @@ const GLOBAL_SHORTCUTS = {
4343
macShortcut: [KEY.ESCAPE],
4444
isEditable: false,
4545
}),
46+
NEXT: ShortcutRegistry.createAndAdd({
47+
id: 'GLOBAL.NEXT',
48+
name: 'Next',
49+
shortcut: [KEY.ENTER],
50+
macShortcut: [KEY.ENTER],
51+
isEditable: false,
52+
}),
53+
PREVIOUS: ShortcutRegistry.createAndAdd({
54+
id: 'GLOBAL.PREVIOUS',
55+
name: 'Previous',
56+
shortcut: [MODIFIER.SHIFT, KEY.ENTER],
57+
macShortcut: [MODIFIER.SHIFT, KEY.ENTER],
58+
isEditable: false,
59+
}),
4660
};
4761

4862
export default GLOBAL_SHORTCUTS;

packages/iris-grid/src/sidebar/visibility-ordering-builder/VisibilityOrderingBuilder.test.tsx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1184,3 +1184,31 @@ test('On drag start/end', () => {
11841184
expect(mockGroupHandler).toBeCalledWith([]);
11851185
expect(mockMoveHandler).toBeCalledWith([{ from: 0, to: 1 }]);
11861186
});
1187+
1188+
test('changeSelectedColumn moves queried column index and loops', () => {
1189+
const builder = React.createRef<VisibilityOrderingBuilder>();
1190+
render(<Builder builderRef={builder} />);
1191+
1192+
builder.current?.searchColumns('TestColumn');
1193+
expect(builder.current?.state.selectedColumns.size).toEqual(10);
1194+
1195+
builder.current?.changeSelectedColumn('back');
1196+
expect(builder.current?.state.queriedColumnIndex).toEqual(9);
1197+
1198+
builder.current?.changeSelectedColumn('forward');
1199+
expect(builder.current?.state.queriedColumnIndex).toEqual(0);
1200+
});
1201+
1202+
test('adjustQueriedIndex sets queriedColumnRange to prevIndex = 9 and nextIndex = 0', () => {
1203+
const builder = React.createRef<VisibilityOrderingBuilder>();
1204+
render(<Builder builderRef={builder} />);
1205+
1206+
builder.current?.searchColumns('TestColumn');
1207+
1208+
builder.current?.adjustQueriedIndex('Test');
1209+
1210+
expect(builder.current?.state.queriedColumnRange).toEqual({
1211+
prevIndex: 9,
1212+
nextIndex: 0,
1213+
});
1214+
});

0 commit comments

Comments
 (0)