Skip to content

Commit

Permalink
feat: add contains ignore case in go to row
Browse files Browse the repository at this point in the history
  • Loading branch information
Ethan Alvizo authored and ethanalvizo committed May 12, 2023
1 parent 02215b6 commit 90227c1
Show file tree
Hide file tree
Showing 5 changed files with 213 additions and 20 deletions.
2 changes: 2 additions & 0 deletions packages/filters/src/Type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ export class Type {

static readonly notContains = 'notContains';

static readonly containsIgnoreCase = 'containsIgnoreCase';

static readonly startsWith = 'startsWith';

static readonly endsWith = 'endsWith';
Expand Down
4 changes: 3 additions & 1 deletion packages/iris-grid/src/GotoRow.scss
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@

.goto-row-text {
min-width: 10ch;
margin: 0;
font-size: 14px;
}

.goto-row-close {
Expand All @@ -36,7 +38,7 @@
max-width: calc($input-padding-x + 12ch + $input-padding-x);
}
select {
max-width: calc($input-padding-x + 20ch + $input-padding-x);
max-width: calc($input-padding-x + 25ch + $input-padding-x);
}
.is-inactive {
color: $gray-400;
Expand Down
167 changes: 167 additions & 0 deletions packages/iris-grid/src/GotoRow.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import dh from '@deephaven/jsapi-shim';
import {
Type as FilterType,
TypeValue as FilterTypeValue,
} from '@deephaven/filters';
import { TableUtils } from '@deephaven/jsapi-utils';
import GotoRow from './GotoRow';
import IrisGridTestUtils from './IrisGridTestUtils';

function makeGotoRow({
gotoRow = '',
gotoRowError = '',
gotoValueError = '',
onGotoRowSubmit = jest.fn(),
model = new IrisGridTestUtils(dh).makeModel(),
onGotoRowNumberChanged = jest.fn(),
onClose = jest.fn(),
isShown = true,
onEntering = jest.fn(),
onEntered = jest.fn(),
onExiting = jest.fn(),
onExited = jest.fn(),
gotoValueSelectedColumnName = '',
gotoValue = '',
gotoValueFilter = FilterType.eq,
onGotoValueSelectedColumnNameChanged = jest.fn(),
onGotoValueSelectedFilterChanged = jest.fn(),
onGotoValueChanged = jest.fn(),
onGotoValueSubmit = jest.fn(),
} = {}) {
return render(
<GotoRow
dh={dh}
gotoRow={gotoRow}
gotoRowError={gotoRowError}
gotoValueError={gotoValueError}
onGotoRowSubmit={onGotoRowSubmit}
model={model}
onGotoRowNumberChanged={onGotoRowNumberChanged}
onClose={onClose}
isShown={isShown}
onEntering={onEntering}
onEntered={onEntered}
onExiting={onExiting}
onExited={onExited}
gotoValueSelectedColumnName={gotoValueSelectedColumnName}
gotoValue={gotoValue}
gotoValueFilter={gotoValueFilter as FilterTypeValue}
onGotoValueSelectedColumnNameChanged={
onGotoValueSelectedColumnNameChanged
}
onGotoValueSelectedFilterChanged={onGotoValueSelectedFilterChanged}
onGotoValueChanged={onGotoValueChanged}
onGotoValueSubmit={onGotoValueSubmit}
/>
);
}

beforeEach(() => {
jest.useFakeTimers();
});

afterEach(() => {
jest.useRealTimers();
});

it('mounts and unmounts properly', () => {
makeGotoRow();
});

describe('Go to row', () => {
it('calls onGotoRowSubmit on Enter key press', async () => {
const user = userEvent.setup({ delay: null });
const onGotoRowSubmitMock = jest.fn();
const component = makeGotoRow({ onGotoRowSubmit: onGotoRowSubmitMock });

const inputElement = screen.getByRole('spinbutton');
await user.type(inputElement, '{Enter}');

expect(onGotoRowSubmitMock).toHaveBeenCalledTimes(1);

component.unmount();
});

it('does not call onGotoRowSubmit on non-Enter key press', async () => {
const user = userEvent.setup({ delay: null });
const onGotoRowSubmitMock = jest.fn();
const component = makeGotoRow({ onGotoRowSubmit: onGotoRowSubmitMock });

const inputElement = screen.getByRole('spinbutton');
await user.type(inputElement, 'a1`');

expect(onGotoRowSubmitMock).not.toHaveBeenCalled();

component.unmount();
});

it('calls onGotoRowNumberChanged on number key press', async () => {
const user = userEvent.setup({ delay: null });
const onGotoRowNumberChangedMock = jest.fn();
const component = makeGotoRow({
onGotoRowNumberChanged: onGotoRowNumberChangedMock,
});

const inputElement = screen.getByRole('spinbutton');
await user.type(inputElement, '1');

expect(onGotoRowNumberChangedMock).toHaveBeenCalledTimes(1);

component.unmount();
});
});

describe('Go to value', () => {
it('calls onGotoValueInputChanged when input value changes', async () => {
const user = userEvent.setup({ delay: null });
const onGotoValueInputChangedMock = jest.fn();
const component = makeGotoRow({
onGotoValueChanged: onGotoValueInputChangedMock,
});

const inputElement = screen.getByPlaceholderText('value');
await user.type(inputElement, 'a');

expect(onGotoValueInputChangedMock).toHaveBeenCalledTimes(1);

component.unmount();
});

it('calls onGotoValueSelectedFilterChanged when select value changes', async () => {
const user = userEvent.setup({ delay: null });
const onGotoValueSelectedFilterChangedMock = jest.fn();

jest
.spyOn(TableUtils, 'getNormalizedType')
.mockImplementation(() => TableUtils.dataType.STRING);

const component = makeGotoRow({
onGotoValueSelectedFilterChanged: onGotoValueSelectedFilterChangedMock,
});

const inputElement = screen.getByLabelText('filter-type-select');
await user.selectOptions(inputElement, [FilterType.contains]);

expect(onGotoValueSelectedFilterChangedMock).toHaveBeenCalledTimes(1);

component.unmount();
});

it('calls onGotoValueSelectedColumnNameChanged when select value changes', async () => {
const user = userEvent.setup({ delay: null });
const onGotoValueSelectedColumnNameChangedMock = jest.fn();
const component = makeGotoRow({
onGotoValueSelectedColumnNameChanged: onGotoValueSelectedColumnNameChangedMock,
});

const inputElement = screen.getByLabelText('column-name-select');
await user.selectOptions(inputElement, ['1']);

expect(onGotoValueSelectedColumnNameChangedMock).toHaveBeenCalledTimes(1);

component.unmount();
});
});
46 changes: 33 additions & 13 deletions packages/iris-grid/src/GotoRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { Button, DateTimeInput } from '@deephaven/components';
import { TableUtils } from '@deephaven/jsapi-utils';
import classNames from 'classnames';
import './GotoRow.scss';
import shortid from 'shortid';
import IrisGridModel from './IrisGridModel';
import IrisGridProxyModel from './IrisGridProxyModel';
import IrisGridBottomBar from './IrisGridBottomBar';
Expand Down Expand Up @@ -86,10 +87,10 @@ function GotoRow({
({ columns } = model.table);
}

const res = 'Row number';

const { rowCount } = model;

const gotoRowInputId = `goto-row-input-${shortid()}`;

const handleGotoValueNumberKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') {
e.stopPropagation();
Expand Down Expand Up @@ -160,6 +161,7 @@ function GotoRow({
}
}}
value={gotoValue}
aria-label="value-input"
/>
</div>
);
Expand All @@ -178,6 +180,7 @@ function GotoRow({
defaultValue={gotoValue}
onChange={onGotoValueInputChanged}
onSubmit={handleGotoValueKeySubmit}
aria-label="value-input"
/>
</div>
);
Expand All @@ -193,19 +196,26 @@ function GotoRow({
);
}}
value={gotoValueFilter}
aria-label="filter-type-select"
>
<option
key={FilterType.eqIgnoreCase}
value={FilterType.eqIgnoreCase}
>
Equals (case-insensitive)
</option>
<option
key={FilterType.containsIgnoreCase}
value={FilterType.containsIgnoreCase}
>
Contains (case-insensitive)
</option>
<option key={FilterType.eq} value={FilterType.eq}>
Equals
</option>
<option key={FilterType.contains} value={FilterType.contains}>
Contains
</option>
<option
key={FilterType.eqIgnoreCase}
value={FilterType.eqIgnoreCase}
>
EqIgnoreCase
</option>
</select>
</div>
<div className="goto-row-input">
Expand All @@ -218,6 +228,7 @@ function GotoRow({
placeholder="value"
onChange={e => onGotoValueInputChanged(e.target.value)}
value={gotoValue}
aria-label="value-input"
/>
</div>
</>
Expand All @@ -231,6 +242,7 @@ function GotoRow({
onGotoValueInputChanged(event.target.value);
}}
value={gotoValue}
aria-label="value-input"
>
<option aria-label="null value" key="null" value="" />
<option key="true" value="true">
Expand All @@ -252,6 +264,7 @@ function GotoRow({
placeholder="value"
onChange={e => onGotoValueInputChanged(e.target.value)}
value={gotoValue}
aria-label="value-input"
/>
</div>
);
Expand Down Expand Up @@ -279,10 +292,13 @@ function GotoRow({
onFocus={() => setIsGotoRowActive(true)}
role="group"
>
<div className="goto-row-text">Go to row</div>
<label className="goto-row-text" htmlFor={gotoRowInputId}>
Go to row
</label>
<div className="goto-row-input">
<input
ref={gotoRowInputRef}
data-testid="goto-row-input"
type="number"
onKeyDown={e => {
if (e.key === 'Enter') {
Expand All @@ -294,11 +310,12 @@ function GotoRow({
className={classNames('form-control', {
'is-invalid': gotoRowError !== '',
})}
placeholder={res}
placeholder="Row number"
onChange={event => {
onGotoRowNumberChanged(event);
}}
value={gotoRow}
id={gotoRowInputId}
/>
</div>
<div className="goto-row-text">
Expand All @@ -314,15 +331,17 @@ function GotoRow({
</div>
{model.isSeekRowAvailable && (
<div className="goto-row-row">
<div
<fieldset
className={classNames('goto-row-wrapper', {
'is-inactive': isGotoRowActive,
})}
onClick={() => setIsGotoRowActive(false)}
onFocus={() => setIsGotoRowActive(false)}
role="group"
>
<div className="goto-row-text">Go to value</div>
<div>
<legend className="goto-row-text">Go to value</legend>
</div>
<div className="goto-row-input">
<select
className="custom-select"
Expand All @@ -331,6 +350,7 @@ function GotoRow({
onGotoValueSelectedColumnNameChanged(columnName);
}}
value={gotoValueSelectedColumnName}
aria-label="column-name-select"
>
{columns.map(column => (
<option key={column.name} value={column.name}>
Expand Down Expand Up @@ -365,7 +385,7 @@ function GotoRow({
{gotoValueError && (
<div className="text-danger">{gotoValueError}</div>
)}
</div>
</fieldset>
</div>
)}
</>
Expand Down
14 changes: 8 additions & 6 deletions packages/iris-grid/src/IrisGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -844,7 +844,7 @@ export class IrisGrid extends Component<IrisGridProps, IrisGridState> {
gotoValueError: '',

gotoValueSelectedColumnName: model.columns[0]?.name ?? '',
gotoValueSelectedFilter: FilterType.eq,
gotoValueSelectedFilter: FilterType.eqIgnoreCase,
gotoValue: '',
columnHeaderGroups: columnHeaderGroups ?? model.initialColumnHeaderGroups,
};
Expand Down Expand Up @@ -3339,10 +3339,12 @@ export class IrisGrid extends Component<IrisGridProps, IrisGridState> {
searchFromRow = 0;
}

const isContains = gotoValueSelectedFilter === FilterType.contains;
const isEquals =
gotoValueSelectedFilter === FilterType.eq ||
gotoValueSelectedFilter === FilterType.eqIgnoreCase;
const isContains =
gotoValueSelectedFilter === FilterType.contains ||
gotoValueSelectedFilter === FilterType.containsIgnoreCase;
const isIgnoreCase =
gotoValueSelectedFilter === FilterType.eqIgnoreCase ||
gotoValueSelectedFilter === FilterType.containsIgnoreCase;

try {
const { formatter } = model;
Expand All @@ -3358,7 +3360,7 @@ export class IrisGrid extends Component<IrisGridProps, IrisGridState> {
selectedColumn,
dh.ValueType.STRING,
inputString,
isEquals,
isIgnoreCase,
isContains,
isBackwards ?? false
);
Expand Down

0 comments on commit 90227c1

Please sign in to comment.