forked from rai-project/mlmodelscope
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor QuickInput to be able to handle text inputs as well
- Loading branch information
1 parent
2c6ca5c
commit 5fff651
Showing
11 changed files
with
452 additions
and
282 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
238 changes: 238 additions & 0 deletions
238
src/components/Experiment/QuickInput/QuickImageInput.test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
}); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.