Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 63 additions & 13 deletions vuu-ui/cypress/support/component/assertions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,21 @@ declare global {
```
* */
(chainer: "not.be.activeDescendant"): Chainable<Subject>;

/**
* Checks if there is a text selection in effect that
* matches the expectation.
*
* @example
```
cy.findByTestId('test-input).should('have.selectionRange',0, 2)
```
* */
(
chainer: "have.selectionRange",
from: number,
to: number,
): Chainable<Subject>;
}
}
}
Expand All @@ -163,7 +178,7 @@ const isHighlighted: ChaiPlugin = (_chai) => {
// make sure it's an Element
new _chai.Assertion(
root.nodeType,
`Expected an Element but got '${String(root)}'`
`Expected an Element but got '${String(root)}'`,
).to.equal(1);

const className = this._obj.attr("class");
Expand All @@ -173,7 +188,7 @@ const isHighlighted: ChaiPlugin = (_chai) => {
`expected root to include CSS class #{exp}, got #{act} instead.`,
`expected root not to have class #{exp}.`,
"saltHighlighted",
className
className,
);
}

Expand All @@ -195,7 +210,7 @@ const hasFocusVisible: ChaiPlugin = (_chai) => {
// make sure it's an Element
new _chai.Assertion(
root.nodeType,
`Expected an Element but got '${String(root)}'`
`Expected an Element but got '${String(root)}'`,
).to.equal(1);

const className = this._obj.attr("class");
Expand All @@ -205,7 +220,7 @@ const hasFocusVisible: ChaiPlugin = (_chai) => {
`expected root to include CSS class #{exp}, got #{act} instead.`,
`expected root not to have class #{exp}.`,
"saltFocusVisible",
className
className,
);
}

Expand All @@ -227,7 +242,7 @@ const hasAriaSelected: ChaiPlugin = (_chai) => {
// make sure it's an Element
new _chai.Assertion(
root.nodeType,
`Expected an Element but got '${String(root)}'`
`Expected an Element but got '${String(root)}'`,
).to.equal(1);

const ariaSelected = this._obj.attr("aria-selected");
Expand All @@ -237,7 +252,7 @@ const hasAriaSelected: ChaiPlugin = (_chai) => {
`expected root to have aria-selected #{exp}, got #{act} instead.`,
`expected root to have aria-selected = #{exp}, got #{act} instead`,
"true",
ariaSelected
ariaSelected,
);
}

Expand All @@ -259,7 +274,7 @@ const isInTheViewport: ChaiPlugin = (_chai, utils) => {
// make sure it's an Element
new _chai.Assertion(
root.nodeType,
`Expected an Element but got '${String(root)}'`
`Expected an Element but got '${String(root)}'`,
).to.equal(1);

const viewportHeight = Cypress.config(`viewportHeight`);
Expand All @@ -269,7 +284,7 @@ const isInTheViewport: ChaiPlugin = (_chai, utils) => {
!(rect.bottom < 0 || rect.top - viewportHeight >= 0),
`expected \n${elementToString(root)} to be in the viewport.`,
`expected \n${elementToString(root)} to not be in the viewport`,
null
null,
);
}

Expand All @@ -293,7 +308,7 @@ const isViewportSize: ChaiPlugin = (_chai, utils) => {
// make sure it's an Element
new _chai.Assertion(
root.nodeType,
`Expected an Element but got '${String(root)}'`
`Expected an Element but got '${String(root)}'`,
).to.equal(1);

const viewportHeight = Cypress.config(`viewportHeight`);
Expand All @@ -303,12 +318,12 @@ const isViewportSize: ChaiPlugin = (_chai, utils) => {
this.assert(
rect.height === viewportHeight && rect.width === viewportWidth,
`expected \n${elementToString(
root
root,
)} to be sized to fill viewport (${viewportWidth} x ${viewportHeight}), actual height ${
rect.height
}, width: ${rect.width} .`,
`expected \n${elementToString(root)} to not be sized to fill viewport.`,
null
null,
);
}

Expand All @@ -331,7 +346,7 @@ const isActiveDescendant: ChaiPlugin = (_chai) => {
// make sure it's an Element
new _chai.Assertion(
root.nodeType,
`Expected an Element but got '${String(root)}'`
`Expected an Element but got '${String(root)}'`,
).to.equal(1);

const id = root.id;
Expand All @@ -340,7 +355,7 @@ const isActiveDescendant: ChaiPlugin = (_chai) => {
$focused.attr("aria-activedescendant") === id,
"expected #{this} to be #{exp}",
"expected #{this} not to be #{exp}",
"active descendant"
"active descendant",
);
});
}
Expand All @@ -351,4 +366,39 @@ const isActiveDescendant: ChaiPlugin = (_chai) => {
// registers our assertion function "isFocused" with Chai
chai.use(isActiveDescendant);

/**
* Checks if the element text includes a text selection range
*
* @example
* cy.findByTestId('test-input).should('have.selectionRange',0,3)
*/
const hasSelectionRange: ChaiPlugin = (_chai, utils) => {
function assertHasSelectedRange(
this: AssertionStatic,
rangeFrom: number,
rangeTo: number,
) {
const root = this._obj.get(0);
// make sure it's an Element
new _chai.Assertion(
root.nodeType,
`Expected an Element but got '${String(root)}'`,
).to.equal(1);

const { selectionStart, selectionEnd } = root;

this.assert(
rangeFrom === selectionStart && rangeTo === selectionEnd,
`expected root to have selectionRange from:${rangeFrom} to: ${rangeTo}, got from: ${selectionStart} to: ${selectionEnd} instead.`,
`expected root not to have selectionRange from:${rangeFrom} to: ${rangeTo}`,
"selectedRange",
);
}

_chai.Assertion.addMethod("selectionRange", assertHasSelectedRange);
};

// registers our assertion function "hasSelectedRange" with Chai
chai.use(hasSelectionRange);

export {};
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ export interface DataItemEditControlProps {
* A table column or form field Descriptor.
*/
dataDescriptor: DataValueDescriptor;
defaultValue?: string | number | readonly string[];
value?: string | number | readonly string[]; //TODO - will be used in VuuTimePicker
errorMessage?: string;
onCommit: CommitHandler<HTMLElement>;
table?: TableSchemaTable;
Expand All @@ -41,7 +39,6 @@ export const getDataItemEditControl = ({
className,
commitWhenCleared,
dataDescriptor,
defaultValue,
errorMessage,
onCommit,
table,
Expand All @@ -63,13 +60,17 @@ export const getDataItemEditControl = ({
/>
);
} else if (isTimeDataValue(dataDescriptor)) {
return (
<VuuTimePicker
className={className}
defaultValue={asTimeString(defaultValue, true)}
onCommit={handleCommitNumber}
/>
);
if (InputProps?.inputProps) {
const { value, onChange } = InputProps.inputProps;
return (
<VuuTimePicker
className={className}
value={asTimeString(value, true)}
onChange={onChange}
onCommit={onCommit}
/>
);
}
} else if (isDateTimeDataValue(dataDescriptor)) {
return (
<VuuDatePicker className={className} onCommit={handleCommitNumber} />
Expand Down
27 changes: 23 additions & 4 deletions vuu-ui/packages/vuu-filters/src/column-filter/ColumnFilter.css
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
.vuuColumnFilter {
border: solid 1px var(--salt-editable-borderColor);
border-radius: 6px;
max-width: 200px;
padding-left: 4px;
padding-right: 4px;
width: var(--vuuColumnFilter-width, fit-content);


.vuuColumnFilter-rangeHigh {
Expand All @@ -14,9 +16,26 @@
position: absolute;
background: var(--salt-separable-primary-borderColor);
/* Accomodate button border */
left: 0;
top: calc(-1 * var(--salt-spacing-100));
bottom: calc(-1 * var(--salt-spacing-100));
left: calc(-1 * var(--salt-spacing-100));
top: -1px;
bottom: -1px;
}

.vuuTimePicker {
border-radius: 6px;
}

.vuuTimePicker + .vuuTimePicker {
margin-left: var(--salt-spacing-100);
}

.vuuTimeInput {
border: none;
outline: none;
}

.vuuTimeInput:focus-visible {
outline: none;
}

}
Expand Down
13 changes: 7 additions & 6 deletions vuu-ui/packages/vuu-filters/src/column-filter/ColumnFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@ import columnFilterCss from "./ColumnFilter.css";
import { getDataItemEditControl } from "@vuu-ui/vuu-data-react";
import { ForwardedRef, forwardRef, ReactElement, useMemo } from "react";
import { VuuTable } from "@vuu-ui/vuu-protocol-types";
import { assertValidOperator, assertValidValue, Operator, useColumnFilter } from "./useColumnFilter";
import {
assertValidOperator,
assertValidValue,
Operator,
useColumnFilter,
} from "./useColumnFilter";
import { ColumnDescriptor } from "@vuu-ui/vuu-table-types";

const classBase = "vuuColumnFilter";
Expand Down Expand Up @@ -126,10 +131,9 @@ export const ColumnFilter = forwardRef(function ColumnFilter(
</Menu>
) : null}
{getDataItemEditControl({
InputProps: { inputProps },
InputProps: { inputProps },
dataDescriptor: column,
onCommit: handleCommit,
defaultValue: Array.isArray(filterValue) ? filterValue[0] : filterValue,
table,
})}
{op === "between"
Expand All @@ -138,9 +142,6 @@ export const ColumnFilter = forwardRef(function ColumnFilter(
InputProps: { inputProps: rangeInputProps },
dataDescriptor: column,
onCommit: handleRangeCommit,
defaultValue: Array.isArray(filterValue)
? filterValue[1]
: filterValue,
table,
})
: null}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export const useColumnFilter = ({

const handleCommit = useCallback<CommitHandler<HTMLElement>>(
(e, newValue) => {
console.log(`[useColumnFilter] handleCommit ${newValue}`);
if (Array.isArray(filterValue.current)) {
filterValue.current = [
newValue as FilterValue,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export const FilterClauseValueEditorTime = (
getInitialState(value),
);

const handleCommit = useCallback<CommitHandler<HTMLElement, number>>(
const handleCommit = useCallback<CommitHandler<HTMLInputElement, string>>(
(e, selectedDateInputValue) => {
console.log("change time");
if (selectedDateInputValue) {
Expand Down
2 changes: 2 additions & 0 deletions vuu-ui/packages/vuu-theme/css/components/components.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
@import url(dropdown.css);
@import url(dialog.css);
@import url(filters.css);
@import url(form-field.css);
@import url(header.css);
@import url(icon.css);
@import url(input.css);
Expand All @@ -16,6 +17,7 @@
@import url(palette.css);
@import url(save-layout-panel.css);
@import url(scrollable.css);
@import url(segmented-button-group.css);
@import url(shell.css);
@import url(split-button.css);
@import url(splitter.css);
Expand Down
7 changes: 7 additions & 0 deletions vuu-ui/packages/vuu-theme/css/components/form-field.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.salt-theme.vuu-theme {

.saltFormField:has(.vuuColumnFilter){
width: fit-content;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.salt-theme.vuu-theme {

.saltSegmentedButtonGroup {
border-radius: 4px;
padding: 1px;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,34 @@ describe("TimeInput", () => {
cy.findByTestId("pre-timeinput").find("input").focus();
cy.realPress("Tab");
cy.findByTestId("timeinput").should("be.focused");
cy.findByTestId("timeinput").should("have.selectionRange", 0, 2);
});
describe("WHEN left/right arrow keys used", () => {
it("THEN right arrow key shifts selection right", () => {
cy.mount(<TestTimeInput defaultValue="00:00:00" />);
cy.findByTestId("pre-timeinput").find("input").focus();
cy.realPress("Tab");
cy.realPress("ArrowRight");
cy.findByTestId("timeinput").should("have.selectionRange", 3, 5);
cy.realPress("ArrowRight");
cy.findByTestId("timeinput").should("have.selectionRange", 6, 8);
cy.realPress("ArrowRight");
cy.findByTestId("timeinput").should("have.selectionRange", 6, 8);
});
it("THEN left arrow key shifts selection left", () => {
cy.mount(<TestTimeInput defaultValue="00:00:00" />);
cy.findByTestId("pre-timeinput").find("input").focus();
cy.realPress("Tab");
cy.realPress("ArrowRight");
cy.realPress("ArrowRight");
cy.findByTestId("timeinput").should("have.selectionRange", 6, 8);
cy.realPress("ArrowLeft");
cy.findByTestId("timeinput").should("have.selectionRange", 3, 5);
cy.realPress("ArrowLeft");
cy.findByTestId("timeinput").should("have.selectionRange", 0, 2);
cy.realPress("ArrowLeft");
cy.findByTestId("timeinput").should("have.selectionRange", 0, 2);
});
});
});
});
Expand Down
Loading
Loading