Skip to content

Commit

Permalink
refactor QuickInput to be able to handle text inputs as well
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewpixo committed Jul 17, 2023
1 parent 2c6ca5c commit 5fff651
Show file tree
Hide file tree
Showing 11 changed files with 452 additions and 282 deletions.
69 changes: 69 additions & 0 deletions src/components/Experiment/QuickInput/QuickImageInput.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import React from "react";
import "./QuickImageInput.scss";
import Task from "../../../helpers/Task";
import useQuickInputControl from "./useQuickInputControl";
import useBEMNaming from "../../../common/useBEMNaming";
import { QuickInputTabContent } from "./QuickInputTabContent";
import { QuickInputTabTitle } from "./QuickInputTabTitle";

export default function QuickImageInput(props) {
const {
tabIsSelected,
selectedInputs,
addInput,
getTabs,
removeInput,
selectTab,
selectInput,
runModel,
} = useQuickInputControl(props);
const { getBlock, getElement } = useBEMNaming("quick-image-input");

const task = Task.getStaticTask(props.model.output.type);
const tabs = getTabs();
return (
<div className={getBlock()}>
{!props.hideHeader && (
<>
<h2 className={getElement("title")}>Try this model</h2>
<div className={getElement("subtitle")}>{task.inputText}</div>
</>
)}
<div className={getElement("tabs")}>
<div className={getElement("tab-titles")} role="tablist">
{tabs.map((tab, index) => (
<QuickInputTabTitle
key={index}
tab={tab}
index={index}
tabIsSelected={tabIsSelected}
selectTab={selectTab}
getElement={getElement}
/>
))}
</div>
{tabs.map((tab, index) => (
<QuickInputTabContent
key={index}
tab={tab}
index={index}
getElement={getElement}
{...props}
removeInput={removeInput}
addInput={addInput}
selectInput={selectInput}
tabIsSelected={tabIsSelected}
selectedInputs={selectedInputs}
/>
))}
</div>
<button
className={getElement("run-model")}
disabled={selectedInputs.length === 0 || selectedInputs[0] === ""}
onClick={() => runModel()}
>
Run model and see results
</button>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
@import "../../../App";

.quick-input {
.quick-image-input {

background: $white;
border-radius: 5px;
Expand Down Expand Up @@ -38,45 +38,45 @@
&__tab-titles {
border-bottom: 1px solid $smokeDarkest;
display: flex;
overflow-x: scroll;
margin-right: -40px;
margin-left: -40px;
margin-right: -40px;
-ms-overflow-style: none;
overflow-x: scroll;
padding-left: 40px;


padding-right: 40px;

scrollbar-width: none;

@include mobileWidth-lg {
margin-right: -16px;
margin-left: -16px;
margin-right: -16px;
padding-left: 16px;
padding-right: 16px;

}

&::-webkit-scrollbar {
display: none;
}

scrollbar-width: none;
-ms-overflow-style: none;
}

&__tab-title {
@include body;
background: transparent;
border: none;
border-bottom: 2px solid transparent;
color: $azul;
padding-bottom: 10px;
border-bottom: 2px solid transparent;
transition: 0.25s;

&:hover {
border-color: $azul;
}

&--selected {
font-weight: bold;
border-bottom: 5px solid $azul;
font-weight: bold;
padding-bottom: 5px;
}

Expand All @@ -99,8 +99,8 @@
@include buttonWithIconAfter("arrow-right-white.svg");

align-self: flex-end;
margin-top: 24px;
line-height: 16px;
margin-top: 24px;

}
}
238 changes: 238 additions & 0 deletions src/components/Experiment/QuickInput/QuickImageInput.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
import React from "react";
import expect from "expect";
import { mount } from "enzyme";
import SampleInputsTab from "./Tabs/SampleInput/SampleInputsTab";
import { SampleInputs } from "./Tabs/SampleInput/SampleInputsTab.test";
import UploadInputsTab from "./Tabs/UploadInput/UploadInputsTab";
import URLInputsTab from "./Tabs/URLInput/URLInputsTab";
import { image_classification } from "../../../helpers/TaskIDs";
import Task from "../../../helpers/Task";
import QuickImageInput from "./QuickImageInput";

describe("Quick Image Input", () => {
const LOAD_SRC = SampleInputs[0];
beforeAll(() => {
Object.defineProperty(global.Image.prototype, "src", {
// Define the property setter
set(src) {
if (src === LOAD_SRC) {
setTimeout(() => this.onload());
} else {
setTimeout(() => this.onerror(new Error("mocked error")));
}
},
});
});

describe("Renders", () => {
let wrapper;
let runModelClicked;
const sampleModel = { output: { type: image_classification } };

beforeEach(() => {
runModelClicked = jest.fn();
wrapper = mount(
<QuickImageInput
onRunModelClicked={runModelClicked}
sampleInputs={SampleInputs}
model={sampleModel}
/>
);
});

it("the correct container class", () => {
expect(wrapper.find(".quick-image-input").hostNodes().length).toBe(1);
});

it("the correct title", () => {
const title = wrapper
.find(".quick-image-input")
.find(".quick-image-input__title");

expect(title.length).toBe(1);
expect(title.text()).toBe("Try this model");
});

it("the correct subtitle", () => {
const subtitle = wrapper
.find(".quick-image-input")
.find(".quick-image-input__subtitle");

expect(subtitle.length).toBe(1);
expect(subtitle.text()).toBe(Task.image_classification.inputText);
});

describe("with tabs", () => {
it("in a tab container", () => {
const container = wrapper
.find(".quick-image-input")
.find(".quick-image-input__tabs");

expect(container.length).toBe(1);
});

it("that have titles", () => {
const titleContainer = wrapper
.find(".quick-image-input")
.find(".quick-image-input__tabs")
.find(".quick-image-input__tab-titles");
const titles = titleContainer.find(
"button.quick-image-input__tab-title"
);
const tabs = wrapper
.find(".quick-image-input")
.find(".quick-image-input__tabs")
.find(".quick-image-input__tab");

expect(titles.length).toBe(3);
expect(titles.at(0).text()).toBe("Sample inputs");
expect(titles.at(1).text()).toBe("Upload");
expect(titles.at(2).text()).toBe("URL");
expect(tabs.length).toBe(3);
});

it("that contain the correct components", () => {
const tabs = wrapper.find(".quick-image-input__tab");

expect(
tabs
.at(0)
.containsMatchingElement(
<SampleInputsTab sampleInputs={SampleInputs} />
)
).toBeTruthy();
expect(
tabs.at(1).containsMatchingElement(<UploadInputsTab />)
).toBeTruthy();
expect(
tabs.at(2).containsMatchingElement(<URLInputsTab />)
).toBeTruthy();
});

it("that have accessibility features", () => {
const titleContainer = wrapper
.find(".quick-image-input")
.find(".quick-image-input__tabs")
.find(".quick-image-input__tab-titles");
const titles = titleContainer.find(
"button.quick-image-input__tab-title"
);
const tabs = wrapper.find(".quick-image-input__tab");

expect(titleContainer.prop("role")).toBe("tablist");
expectTitleAccessibilityWithId(titles.at(0), "sample-input");
expectTitleAccessibilityWithId(titles.at(1), "upload-input");
expectTitleAccessibilityWithId(titles.at(2), "url-input");

expectTabAccessibilityWithId(tabs.at(0), "sample-input");
expectTabAccessibilityWithId(tabs.at(1), "upload-input");
expectTabAccessibilityWithId(tabs.at(2), "url-input");

function expectTitleAccessibilityWithId(title, id) {
expect(title.prop("role")).toBe("tab");
expect(title.prop("aria-controls")).toBe(`${id}-panel`);
expect(title.prop("id")).toBe(`${id}`);
}

function expectTabAccessibilityWithId(tab, id) {
expect(tab.prop("role")).toBe("tabpanel");
expect(tab.prop("aria-labelledby")).toBe(`${id}`);
expect(tab.prop("id")).toBe(`${id}-panel`);
}
});

it("where the first tab is selected by default", () => {
const titles = wrapper.find(".quick-image-input__tab-title");
const tabs = wrapper.find(".quick-image-input__tab");
const selectedTitle = wrapper.find(
".quick-image-input__tab-title--selected"
);
const selectedTab = wrapper.find(".quick-image-input__tab--selected");

expect(titles.at(0).prop("aria-selected")).toBe("true");
expect(titles.at(0).prop("id")).toBe(selectedTitle.prop("id"));
expect(titles.at(1).prop("aria-selected")).toBe("false");
expect(titles.at(2).prop("aria-selected")).toBe("false");
expect(tabs.at(0).prop("id")).toBe(selectedTab.prop("id"));
});

it("where clicking a tab title selects that tab", () => {
wrapper.find(".quick-image-input__tab-title").at(1).simulate("click");
const titles = wrapper.find(".quick-image-input__tab-title");
const tabs = wrapper.find(".quick-image-input__tab");
const selectedTitle = wrapper.find(
".quick-image-input__tab-title--selected"
);
const selectedTab = wrapper.find(".quick-image-input__tab--selected");

expect(titles.at(0).prop("aria-selected")).toBe("false");
expect(titles.at(1).prop("aria-selected")).toBe("true");
expect(titles.at(1).prop("id")).toBe(selectedTitle.prop("id"));
expect(tabs.at(1).prop("id")).toBe(selectedTab.prop("id"));
});
});

describe("a run button", () => {
it("with the correct label", () => {
const button = wrapper
.find(".quick-image-input")
.find("button.quick-image-input__run-model");

expect(button.length).toBe(1);
expect(button.text()).toBe("Run model and see results");
});

it("that is disabled by default", () => {
const button = wrapper.find(".quick-image-input__run-model");

expect(button.prop("disabled")).toBeTruthy();
});

it("that becomes enabled when an input is selected", () => {
wrapper.find("img").first().simulate("click");
wrapper.update();
const button = wrapper.find(".quick-image-input__run-model");

expect(button.prop("disabled")).toBeFalsy();
});

it("where clicking calls the provided onRunModelClicked method with the selected input", () => {
wrapper.find("img").first().simulate("click");
wrapper.update();
wrapper.find(".quick-image-input__run-model").simulate("click");

expect(runModelClicked.mock.calls.length).toBe(1);
expect(runModelClicked.mock.calls[0][0][0]).toBe(
"https://example.com/sample1.jpg"
);
});
});

describe("a Sample Inputs Tab", () => {
it("that calls back to selectInput()", () => {
wrapper.find("img").first().simulate("click");
wrapper.update();

expect(
wrapper.find(".quick-image-input__run-model").prop("disabled")
).toBeFalsy();
});
});

describe("a URL Inputs Tab", () => {
it("that calls back to selectInput()", async () => {
wrapper
.find(".url-inputs__url")
.simulate("change", { target: { value: SampleInputs[0].src } });

await new Promise((resolve) => setTimeout(resolve, 10));

wrapper.update();

expect(
wrapper.find(".quick-image-input__run-model").prop("disabled")
).toBeFalsy();
});
});
});
});
Loading

0 comments on commit 5fff651

Please sign in to comment.