Skip to content

Commit d67712e

Browse files
ethanalvizoEthan Alvizo
andauthored
feat: add contains ignore case in go to row (#1291)
Closes #1274 --------- Co-authored-by: Ethan Alvizo <ethanalvizo@Ethans-Laptop.local>
1 parent 4b3cd62 commit d67712e

5 files changed

Lines changed: 209 additions & 17 deletions

File tree

packages/filters/src/Type.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ export class Type {
3636

3737
static readonly notContains = 'notContains';
3838

39+
static readonly containsIgnoreCase = 'containsIgnoreCase';
40+
3941
static readonly startsWith = 'startsWith';
4042

4143
static readonly endsWith = 'endsWith';

packages/iris-grid/src/GotoRow.scss

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626

2727
.goto-row-text {
2828
min-width: 10ch;
29+
margin: 0;
30+
font-size: 14px;
2931
}
3032

3133
.goto-row-close {
@@ -36,7 +38,7 @@
3638
max-width: calc($input-padding-x + 12ch + $input-padding-x);
3739
}
3840
select {
39-
max-width: calc($input-padding-x + 20ch + $input-padding-x);
41+
max-width: calc($input-padding-x + 25ch + $input-padding-x);
4042
}
4143
.is-inactive {
4244
color: $gray-400;
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
import React from 'react';
2+
import { render, screen } from '@testing-library/react';
3+
import userEvent from '@testing-library/user-event';
4+
import dh from '@deephaven/jsapi-shim';
5+
import {
6+
Type as FilterType,
7+
TypeValue as FilterTypeValue,
8+
} from '@deephaven/filters';
9+
import { TableUtils } from '@deephaven/jsapi-utils';
10+
import GotoRow from './GotoRow';
11+
import IrisGridTestUtils from './IrisGridTestUtils';
12+
13+
function makeGotoRow({
14+
gotoRow = '',
15+
gotoRowError = '',
16+
gotoValueError = '',
17+
onGotoRowSubmit = jest.fn(),
18+
model = new IrisGridTestUtils(dh).makeModel(),
19+
onGotoRowNumberChanged = jest.fn(),
20+
onClose = jest.fn(),
21+
isShown = true,
22+
onEntering = jest.fn(),
23+
onEntered = jest.fn(),
24+
onExiting = jest.fn(),
25+
onExited = jest.fn(),
26+
gotoValueSelectedColumnName = '',
27+
gotoValue = '',
28+
gotoValueFilter = FilterType.eq,
29+
onGotoValueSelectedColumnNameChanged = jest.fn(),
30+
onGotoValueSelectedFilterChanged = jest.fn(),
31+
onGotoValueChanged = jest.fn(),
32+
onGotoValueSubmit = jest.fn(),
33+
} = {}) {
34+
return render(
35+
<GotoRow
36+
dh={dh}
37+
gotoRow={gotoRow}
38+
gotoRowError={gotoRowError}
39+
gotoValueError={gotoValueError}
40+
onGotoRowSubmit={onGotoRowSubmit}
41+
model={model}
42+
onGotoRowNumberChanged={onGotoRowNumberChanged}
43+
onClose={onClose}
44+
isShown={isShown}
45+
onEntering={onEntering}
46+
onEntered={onEntered}
47+
onExiting={onExiting}
48+
onExited={onExited}
49+
gotoValueSelectedColumnName={gotoValueSelectedColumnName}
50+
gotoValue={gotoValue}
51+
gotoValueFilter={gotoValueFilter as FilterTypeValue}
52+
onGotoValueSelectedColumnNameChanged={
53+
onGotoValueSelectedColumnNameChanged
54+
}
55+
onGotoValueSelectedFilterChanged={onGotoValueSelectedFilterChanged}
56+
onGotoValueChanged={onGotoValueChanged}
57+
onGotoValueSubmit={onGotoValueSubmit}
58+
/>
59+
);
60+
}
61+
62+
beforeEach(() => {
63+
jest.useFakeTimers();
64+
});
65+
66+
afterEach(() => {
67+
jest.useRealTimers();
68+
});
69+
70+
it('mounts and unmounts properly', () => {
71+
makeGotoRow();
72+
});
73+
74+
describe('Go to row', () => {
75+
it('calls onGotoRowSubmit on Enter key press', async () => {
76+
const user = userEvent.setup({ delay: null });
77+
const onGotoRowSubmitMock = jest.fn();
78+
const component = makeGotoRow({ onGotoRowSubmit: onGotoRowSubmitMock });
79+
80+
const inputElement = screen.getByRole('spinbutton');
81+
await user.type(inputElement, '{Enter}');
82+
83+
expect(onGotoRowSubmitMock).toHaveBeenCalledTimes(1);
84+
85+
component.unmount();
86+
});
87+
88+
it('does not call onGotoRowSubmit on non-Enter key press', async () => {
89+
const user = userEvent.setup({ delay: null });
90+
const onGotoRowSubmitMock = jest.fn();
91+
const component = makeGotoRow({ onGotoRowSubmit: onGotoRowSubmitMock });
92+
93+
const inputElement = screen.getByRole('spinbutton');
94+
await user.type(inputElement, 'a1`');
95+
96+
expect(onGotoRowSubmitMock).not.toHaveBeenCalled();
97+
98+
component.unmount();
99+
});
100+
101+
it('calls onGotoRowNumberChanged on number key press', async () => {
102+
const user = userEvent.setup({ delay: null });
103+
const onGotoRowNumberChangedMock = jest.fn();
104+
const component = makeGotoRow({
105+
onGotoRowNumberChanged: onGotoRowNumberChangedMock,
106+
});
107+
108+
const inputElement = screen.getByRole('spinbutton');
109+
await user.type(inputElement, '1');
110+
111+
expect(onGotoRowNumberChangedMock).toHaveBeenCalledTimes(1);
112+
113+
component.unmount();
114+
});
115+
});
116+
117+
describe('Go to value', () => {
118+
it('calls onGotoValueInputChanged when input value changes', async () => {
119+
const user = userEvent.setup({ delay: null });
120+
const onGotoValueInputChangedMock = jest.fn();
121+
const component = makeGotoRow({
122+
onGotoValueChanged: onGotoValueInputChangedMock,
123+
});
124+
125+
const inputElement = screen.getByPlaceholderText('value');
126+
await user.type(inputElement, 'a');
127+
128+
expect(onGotoValueInputChangedMock).toHaveBeenCalledTimes(1);
129+
130+
component.unmount();
131+
});
132+
133+
it('calls onGotoValueSelectedFilterChanged when select value changes', async () => {
134+
const user = userEvent.setup({ delay: null });
135+
const onGotoValueSelectedFilterChangedMock = jest.fn();
136+
137+
jest
138+
.spyOn(TableUtils, 'getNormalizedType')
139+
.mockImplementation(() => TableUtils.dataType.STRING);
140+
141+
const component = makeGotoRow({
142+
onGotoValueSelectedFilterChanged: onGotoValueSelectedFilterChangedMock,
143+
});
144+
145+
const inputElement = screen.getByLabelText('filter-type-select');
146+
await user.selectOptions(inputElement, [FilterType.contains]);
147+
148+
expect(onGotoValueSelectedFilterChangedMock).toHaveBeenCalledTimes(1);
149+
150+
component.unmount();
151+
});
152+
153+
it('calls onGotoValueSelectedColumnNameChanged when select value changes', async () => {
154+
const user = userEvent.setup({ delay: null });
155+
const onGotoValueSelectedColumnNameChangedMock = jest.fn();
156+
const component = makeGotoRow({
157+
onGotoValueSelectedColumnNameChanged: onGotoValueSelectedColumnNameChangedMock,
158+
});
159+
160+
const inputElement = screen.getByLabelText('column-name-select');
161+
await user.selectOptions(inputElement, ['1']);
162+
163+
expect(onGotoValueSelectedColumnNameChangedMock).toHaveBeenCalledTimes(1);
164+
165+
component.unmount();
166+
});
167+
});

packages/iris-grid/src/GotoRow.tsx

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import React, {
55
KeyboardEvent,
66
ReactElement,
77
useEffect,
8+
useMemo,
89
useRef,
910
useState,
1011
} from 'react';
@@ -17,6 +18,7 @@ import { Button, DateTimeInput } from '@deephaven/components';
1718
import { TableUtils } from '@deephaven/jsapi-utils';
1819
import classNames from 'classnames';
1920
import './GotoRow.scss';
21+
import shortid from 'shortid';
2022
import IrisGridModel from './IrisGridModel';
2123
import IrisGridProxyModel from './IrisGridProxyModel';
2224
import IrisGridBottomBar from './IrisGridBottomBar';
@@ -84,10 +86,10 @@ function GotoRow({
8486
({ columns } = model.table);
8587
}
8688

87-
const res = 'Row number';
88-
8989
const { dh, rowCount } = model;
9090

91+
const gotoRowInputId = useMemo(() => `goto-row-input-${shortid()}`, []);
92+
9193
const handleGotoValueNumberKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
9294
if (e.key === 'Enter') {
9395
e.stopPropagation();
@@ -158,6 +160,7 @@ function GotoRow({
158160
}
159161
}}
160162
value={gotoValue}
163+
aria-label="Value Input"
161164
/>
162165
</div>
163166
);
@@ -176,6 +179,7 @@ function GotoRow({
176179
defaultValue={gotoValue}
177180
onChange={onGotoValueInputChanged}
178181
onSubmit={handleGotoValueKeySubmit}
182+
aria-label="Value Input"
179183
/>
180184
</div>
181185
);
@@ -191,19 +195,26 @@ function GotoRow({
191195
);
192196
}}
193197
value={gotoValueFilter}
198+
aria-label="filter-type-select"
194199
>
200+
<option
201+
key={FilterType.eqIgnoreCase}
202+
value={FilterType.eqIgnoreCase}
203+
>
204+
Equals (case-insensitive)
205+
</option>
206+
<option
207+
key={FilterType.containsIgnoreCase}
208+
value={FilterType.containsIgnoreCase}
209+
>
210+
Contains (case-insensitive)
211+
</option>
195212
<option key={FilterType.eq} value={FilterType.eq}>
196213
Equals
197214
</option>
198215
<option key={FilterType.contains} value={FilterType.contains}>
199216
Contains
200217
</option>
201-
<option
202-
key={FilterType.eqIgnoreCase}
203-
value={FilterType.eqIgnoreCase}
204-
>
205-
EqIgnoreCase
206-
</option>
207218
</select>
208219
</div>
209220
<div className="goto-row-input">
@@ -216,6 +227,7 @@ function GotoRow({
216227
placeholder="value"
217228
onChange={e => onGotoValueInputChanged(e.target.value)}
218229
value={gotoValue}
230+
aria-label="Value Input"
219231
/>
220232
</div>
221233
</>
@@ -229,6 +241,7 @@ function GotoRow({
229241
onGotoValueInputChanged(event.target.value);
230242
}}
231243
value={gotoValue}
244+
aria-label="Value Input"
232245
>
233246
<option aria-label="null value" key="null" value="" />
234247
<option key="true" value="true">
@@ -250,6 +263,7 @@ function GotoRow({
250263
placeholder="value"
251264
onChange={e => onGotoValueInputChanged(e.target.value)}
252265
value={gotoValue}
266+
aria-label="Value Input"
253267
/>
254268
</div>
255269
);
@@ -277,10 +291,13 @@ function GotoRow({
277291
onFocus={() => setIsGotoRowActive(true)}
278292
role="group"
279293
>
280-
<div className="goto-row-text">Go to row</div>
294+
<label className="goto-row-text" htmlFor={gotoRowInputId}>
295+
Go to row
296+
</label>
281297
<div className="goto-row-input">
282298
<input
283299
ref={gotoRowInputRef}
300+
data-testid="goto-row-input"
284301
type="number"
285302
onKeyDown={e => {
286303
if (e.key === 'Enter') {
@@ -292,11 +309,12 @@ function GotoRow({
292309
className={classNames('form-control', {
293310
'is-invalid': gotoRowError !== '',
294311
})}
295-
placeholder={res}
312+
placeholder="Row number"
296313
onChange={event => {
297314
onGotoRowNumberChanged(event);
298315
}}
299316
value={gotoRow}
317+
id={gotoRowInputId}
300318
/>
301319
</div>
302320
<div className="goto-row-text">
@@ -329,6 +347,7 @@ function GotoRow({
329347
onGotoValueSelectedColumnNameChanged(columnName);
330348
}}
331349
value={gotoValueSelectedColumnName}
350+
aria-label="column-name-select"
332351
>
333352
{columns.map(column => (
334353
<option key={column.name} value={column.name}>

packages/iris-grid/src/IrisGrid.tsx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -843,7 +843,7 @@ export class IrisGrid extends Component<IrisGridProps, IrisGridState> {
843843
gotoValueError: '',
844844

845845
gotoValueSelectedColumnName: model.columns[0]?.name ?? '',
846-
gotoValueSelectedFilter: FilterType.eq,
846+
gotoValueSelectedFilter: FilterType.eqIgnoreCase,
847847
gotoValue: '',
848848
columnHeaderGroups: columnHeaderGroups ?? model.initialColumnHeaderGroups,
849849
};
@@ -3336,10 +3336,12 @@ export class IrisGrid extends Component<IrisGridProps, IrisGridState> {
33363336
searchFromRow = 0;
33373337
}
33383338

3339-
const isContains = gotoValueSelectedFilter === FilterType.contains;
3340-
const isEquals =
3341-
gotoValueSelectedFilter === FilterType.eq ||
3342-
gotoValueSelectedFilter === FilterType.eqIgnoreCase;
3339+
const isContains =
3340+
gotoValueSelectedFilter === FilterType.contains ||
3341+
gotoValueSelectedFilter === FilterType.containsIgnoreCase;
3342+
const isIgnoreCase =
3343+
gotoValueSelectedFilter === FilterType.eqIgnoreCase ||
3344+
gotoValueSelectedFilter === FilterType.containsIgnoreCase;
33433345

33443346
try {
33453347
const { formatter } = model;
@@ -3355,7 +3357,7 @@ export class IrisGrid extends Component<IrisGridProps, IrisGridState> {
33553357
selectedColumn,
33563358
dh.ValueType.STRING,
33573359
inputString,
3358-
isEquals,
3360+
isIgnoreCase,
33593361
isContains,
33603362
isBackwards ?? false
33613363
);

0 commit comments

Comments
 (0)