Skip to content
Snippets Groups Projects
Unverified Commit 06918d8a authored by Uladzimir Havenchyk's avatar Uladzimir Havenchyk Committed by GitHub
Browse files

[Filter] Save value on blur in text/number based widgets with sql query (#33374)


* Add test

* Update imports

* Add value normalization and save value on blur

* Update e2e/test/scenarios/dashboard-filters/dashboard-filters-sql-number.cy.spec.js

Co-authored-by: default avatarMahatthana (Kelvin) Nomsawadi <me@bboykelvin.dev>

* Update e2e/test/scenarios/dashboard-filters/dashboard-filters-sql-number.cy.spec.js

Co-authored-by: default avatarMahatthana (Kelvin) Nomsawadi <me@bboykelvin.dev>

* Use helper

* Simplify test

---------

Co-authored-by: default avatarMahatthana (Kelvin) Nomsawadi <me@bboykelvin.dev>
parent da85276c
No related branches found
No related tags found
No related merge requests found
......@@ -5,8 +5,10 @@ import {
filterWidget,
editDashboard,
saveDashboard,
getDashboardCard,
setFilter,
visitQuestion,
sidebar,
visitDashboard,
} from "e2e/support/helpers";
......@@ -38,7 +40,7 @@ describe("scenarios > dashboard > filters > SQL > text/category", () => {
setFilter("Number", filter);
cy.findByText("Select…").click();
clickSelect();
popover().contains(filter).click();
});
......@@ -60,15 +62,13 @@ describe("scenarios > dashboard > filters > SQL > text/category", () => {
);
});
it(`should work when set as the default filter`, () => {
it("should work when set as the default filter", () => {
setFilter("Number", "Equal to");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Default value").next().click();
sidebar().findByText("Default value").next().click();
addWidgetNumberFilter("3.8");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Select…").click();
clickSelect();
popover().contains("Equal to").click();
saveDashboard();
......@@ -90,3 +90,106 @@ describe("scenarios > dashboard > filters > SQL > text/category", () => {
});
});
});
describe("scenarios > dashboard > filters > SQL > number", () => {
const questionDetails = {
name: "Question 1",
native: {
query:
"SELECT * from products where true [[ and price > {{price}}]] [[ and rating > {{rating}} ]] limit 5;",
"template-tags": {
price: {
type: "number",
name: "price",
id: "b22a5ce2-fe1d-44e3-8df4-f8951f7921bc",
"display-name": "Price",
},
rating: {
type: "number",
name: "rating",
id: "68821a54-f0f3-4f09-8c32-6f7c0e5e5399",
"display-name": "Rating",
},
},
},
};
const filterDetails = [
{
name: "Rating",
slug: "rating",
id: "10c0d4ba",
type: "number/=",
sectionId: "number",
},
{
name: "Price",
slug: "price",
id: "88b1a9dd",
type: "number/=",
sectionId: "number",
},
];
const parameterMapping = filterDetails.map(filter => ({
parameter_id: filter.id,
target: ["variable", ["template-tag", filter.slug]],
}));
const dashboardDetails = {
name: "Dashboard #31975",
parameters: filterDetails,
};
const dashcardDetails = {
row: 0,
col: 0,
size_x: 16,
size_y: 8,
};
beforeEach(() => {
restore();
cy.signInAsAdmin();
cy.createNativeQuestionAndDashboard({
questionDetails,
dashboardDetails,
}).then(({ body: { id, card_id, dashboard_id } }) => {
cy.request("PUT", `/api/dashboard/${dashboard_id}/cards`, {
cards: [
{
id,
card_id,
...dashcardDetails,
parameter_mappings: parameterMapping.map(mapping => ({
...mapping,
card_id,
})),
},
],
});
visitDashboard(dashboard_id);
});
});
it("should keep filter value on blur (metabase#31975)", () => {
cy.findByPlaceholderText("Price").type("95").blur();
cy.findByPlaceholderText("Rating").type("3.8").blur();
cy.findAllByTestId("table-row")
.should("have.length", 2)
// first line price
.and("contain", "98.82")
// first line rating
.and("contain", "4.3")
// second line price
.and("contain", "95.93")
// second line rating
.and("contain", "4.4");
});
});
function clickSelect() {
getDashboardCard().findByText("Select…").click();
}
import type { ComponentStory } from "@storybook/react";
import { useArgs } from "@storybook/client-api";
import TextWidget from "./TextWidget";
import { TextWidget } from "./TextWidget";
export default {
title: "Parameters/TextWidget",
......
......@@ -2,7 +2,6 @@ import { Component } from "react";
import ReactDOM from "react-dom";
import { t } from "ttag";
import { forceRedraw } from "metabase/lib/dom";
import { KEYCODE_ENTER, KEYCODE_ESCAPE } from "metabase/lib/keyboard";
type Props = {
value: string | number;
......@@ -20,7 +19,7 @@ type State = {
isFocused: boolean;
};
class TextWidget extends Component<Props, State> {
export class TextWidget extends Component<Props, State> {
static defaultProps = {
isEditing: false,
commitImmediately: false,
......@@ -82,9 +81,9 @@ class TextWidget extends Component<Props, State> {
}}
onKeyUp={e => {
const target = e.target as HTMLInputElement;
if (e.keyCode === KEYCODE_ESCAPE) {
if (e.key === "Escape") {
target.blur();
} else if (e.keyCode === KEYCODE_ENTER) {
} else if (e.key === "Enter") {
setValue(this.state.value ?? null);
target.blur();
}
......@@ -94,7 +93,9 @@ class TextWidget extends Component<Props, State> {
}}
onBlur={() => {
changeFocus(false);
this.setState({ value: this.props.value });
if (this.state.value !== this.props.value) {
setValue(this.state.value ?? null);
}
}}
placeholder={isEditing ? t`Enter a default value…` : defaultPlaceholder}
disabled={disabled}
......@@ -102,6 +103,3 @@ class TextWidget extends Component<Props, State> {
);
}
}
// eslint-disable-next-line import/no-default-export -- deprecated usage
export default TextWidget;
import { useState } from "react";
import { fireEvent, render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import TextWidget from "./TextWidget";
import { TextWidget } from "./TextWidget";
const TextInputWithStateWrapper = ({ value }: { value?: number | string }) => {
const [val, setVal] = useState<number | string | null>(value ?? "");
......
// eslint-disable-next-line import/no-default-export -- deprecated usage
export { default } from "./TextWidget";
export { TextWidget } from "./TextWidget";
......@@ -189,6 +189,7 @@ export const setParameterValue = createThunkAction(
SET_PARAMETER_VALUE,
(parameterId, value) => (_dispatch, getState) => {
const isSettingDraftParameterValues = !getIsAutoApplyFilters(getState());
return {
id: parameterId,
value: normalizeValue(value),
......@@ -202,6 +203,10 @@ function normalizeValue(value) {
return null;
}
if (value === "") {
return null;
}
return value;
}
......
......@@ -16,7 +16,7 @@ import DateRelativeWidget from "metabase/components/DateRelativeWidget";
import DateMonthYearWidget from "metabase/components/DateMonthYearWidget";
import DateQuarterYearWidget from "metabase/components/DateQuarterYearWidget";
import { DateAllOptionsWidget } from "metabase/components/DateAllOptionsWidget";
import TextWidget from "metabase/components/TextWidget";
import { TextWidget } from "metabase/components/TextWidget";
import WidgetStatusIcon from "metabase/parameters/components/WidgetStatusIcon";
import FormattedParameterValue from "metabase/parameters/components/FormattedParameterValue";
import NumberInputWidget from "metabase/parameters/components/widgets/NumberInputWidget";
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment