From 063ceb215f858a8e2a5bde85d8f9ca24240894c6 Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Mon, 21 Sep 2020 15:20:41 +0900 Subject: Separate input component --- src/settings/components/form/KeymapsForm.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/settings/components/form') diff --git a/src/settings/components/form/KeymapsForm.tsx b/src/settings/components/form/KeymapsForm.tsx index b9af0df..25578ad 100644 --- a/src/settings/components/form/KeymapsForm.tsx +++ b/src/settings/components/form/KeymapsForm.tsx @@ -1,6 +1,6 @@ import "./KeymapsForm.scss"; import React from "react"; -import Input from "../ui/Input"; +import Text from "../ui/Text"; import keymaps from "../../keymaps"; import { FormKeymaps } from "../../../shared/SettingData"; @@ -27,8 +27,7 @@ class KeymapsForm extends React.Component { {group.map(([name, label]) => { const value = values[name] || ""; return ( - Date: Mon, 21 Sep 2020 21:09:01 +0900 Subject: Introduce styled-components on form fields --- src/settings/components/form/BlacklistForm.scss | 9 -- src/settings/components/form/BlacklistForm.tsx | 80 ++++++++----- src/settings/components/form/KeymapsForm.scss | 11 -- src/settings/components/form/KeymapsForm.tsx | 22 +++- .../components/form/PartialBlacklistForm.scss | 28 ----- .../components/form/PartialBlacklistForm.tsx | 117 ++++++++++++------- src/settings/components/form/PropertiesForm.scss | 12 -- src/settings/components/form/PropertiesForm.tsx | 33 ++++-- src/settings/components/form/SearchForm.scss | 28 ----- src/settings/components/form/SearchForm.tsx | 126 +++++++++++++-------- 10 files changed, 248 insertions(+), 218 deletions(-) delete mode 100644 src/settings/components/form/BlacklistForm.scss delete mode 100644 src/settings/components/form/KeymapsForm.scss delete mode 100644 src/settings/components/form/PartialBlacklistForm.scss delete mode 100644 src/settings/components/form/PropertiesForm.scss delete mode 100644 src/settings/components/form/SearchForm.scss (limited to 'src/settings/components/form') diff --git a/src/settings/components/form/BlacklistForm.scss b/src/settings/components/form/BlacklistForm.scss deleted file mode 100644 index a230d0d..0000000 --- a/src/settings/components/form/BlacklistForm.scss +++ /dev/null @@ -1,9 +0,0 @@ -.form-blacklist-form { - &-row { - display: flex; - - .column-url { - flex: 1; - } - } -} diff --git a/src/settings/components/form/BlacklistForm.tsx b/src/settings/components/form/BlacklistForm.tsx index 859cb9f..4d794f4 100644 --- a/src/settings/components/form/BlacklistForm.tsx +++ b/src/settings/components/form/BlacklistForm.tsx @@ -1,9 +1,29 @@ -import "./BlacklistForm.scss"; +import styled from "styled-components"; import AddButton from "../ui/AddButton"; import DeleteButton from "../ui/DeleteButton"; import React from "react"; import Blacklist, { BlacklistItem } from "../../../shared/settings/Blacklist"; +const Grid = styled.div``; + +const GridRow = styled.div` + display: flex; +`; + +const GridCell = styled.div<{ grow?: number }>` + &:nth-child(1) { + flex-grow: 1; + } + &:nth-child(2) { + flex-shrink: 1; + } +`; + +const Input = styled.input` + width: 100%; + box-sizing: border-box; +`; + interface Props { value: Blacklist; onChange: (value: Blacklist) => void; @@ -19,37 +39,43 @@ class BlacklistForm extends React.Component { render() { return ( -
- {this.props.value.items.map((item, index) => { - if (item.partial) { - return null; - } - return ( -
- - -
- ); - })} + <> + + {this.props.value.items.map((item, index) => { + if (item.partial) { + return null; + } + return ( + + + + + + + + + ); + })} + -
+ ); } diff --git a/src/settings/components/form/KeymapsForm.scss b/src/settings/components/form/KeymapsForm.scss deleted file mode 100644 index 1a4e5cd..0000000 --- a/src/settings/components/form/KeymapsForm.scss +++ /dev/null @@ -1,11 +0,0 @@ -.form-keymaps-form { - column-count: 3; - - &-field-group { - margin-top: 24px; - } - - &-field-group:first-of-type { - margin-top: 24px; - } -} diff --git a/src/settings/components/form/KeymapsForm.tsx b/src/settings/components/form/KeymapsForm.tsx index 25578ad..6582529 100644 --- a/src/settings/components/form/KeymapsForm.tsx +++ b/src/settings/components/form/KeymapsForm.tsx @@ -1,9 +1,21 @@ -import "./KeymapsForm.scss"; import React from "react"; +import styled from "styled-components"; import Text from "../ui/Text"; import keymaps from "../../keymaps"; import { FormKeymaps } from "../../../shared/SettingData"; +const Grid = styled.div` + column-count: 3; +`; + +const FieldGroup = styled.div` + margin-top: 24px; + + &:first-of-type { + margin-top: 24px; + } +`; + interface Props { value: FormKeymaps; onChange: (e: FormKeymaps) => void; @@ -20,10 +32,10 @@ class KeymapsForm extends React.Component { render() { const values = this.props.value.toJSON(); return ( -
+ {keymaps.fields.map((group, index) => { return ( -
+ {group.map(([name, label]) => { const value = values[name] || ""; return ( @@ -38,10 +50,10 @@ class KeymapsForm extends React.Component { /> ); })} -
+ ); })} -
+ ); } diff --git a/src/settings/components/form/PartialBlacklistForm.scss b/src/settings/components/form/PartialBlacklistForm.scss deleted file mode 100644 index caf6f93..0000000 --- a/src/settings/components/form/PartialBlacklistForm.scss +++ /dev/null @@ -1,28 +0,0 @@ -.form-partial-blacklist-form { - @mixin row-base { - display: flex; - - .column-url { - flex: 5; - min-width: 0; - } - .column-keys { - flex: 1; - min-width: 0; - } - .column-delete { - flex: 1; - min-width: 0; - } - } - - &-header { - @include row-base; - - font-weight: bold; - } - - &-row { - @include row-base; - } -} diff --git a/src/settings/components/form/PartialBlacklistForm.tsx b/src/settings/components/form/PartialBlacklistForm.tsx index 95beee8..4c6bd35 100644 --- a/src/settings/components/form/PartialBlacklistForm.tsx +++ b/src/settings/components/form/PartialBlacklistForm.tsx @@ -1,9 +1,36 @@ -import "./PartialBlacklistForm.scss"; +import React from "react"; +import styled from "styled-components"; import AddButton from "../ui/AddButton"; import DeleteButton from "../ui/DeleteButton"; -import React from "react"; import Blacklist, { BlacklistItem } from "../../../shared/settings/Blacklist"; +const Grid = styled.div``; + +const GridRow = styled.div` + display: flex; +`; + +const GridCell = styled.div<{ grow?: number }>` + &:nth-child(1) { + flex-grow: 5; + } + + &:nth-child(2) { + flex-shrink: 1; + min-width: 20%; + max-width: 20%; + } + + &:nth-child(3) { + flex-shrink: 1; + } +`; + +const Input = styled.input` + width: 100%; + box-sizing: border-box; +`; + interface Props { value: Blacklist; onChange: (value: Blacklist) => void; @@ -19,50 +46,58 @@ class PartialBlacklistForm extends React.Component { render() { return ( -
-
-
URL
-
Keys
-
- {this.props.value.items.map((item, index) => { - if (!item.partial) { - return null; - } - return ( -
- - - -
- ); - })} + <> + + + URL + Keys + + {this.props.value.items.map((item, index) => { + if (!item.partial) { + return null; + } + return ( + + + + + + + + + + + + ); + })} + -
+ ); } diff --git a/src/settings/components/form/PropertiesForm.scss b/src/settings/components/form/PropertiesForm.scss deleted file mode 100644 index 7c9e167..0000000 --- a/src/settings/components/form/PropertiesForm.scss +++ /dev/null @@ -1,12 +0,0 @@ -.form-properties-form { - &-row { - .column-name { - display: inline-block; - min-width: 5rem; - font-weight: bold; - } - .column-input { - line-height: 2.2rem; - } - } -} diff --git a/src/settings/components/form/PropertiesForm.tsx b/src/settings/components/form/PropertiesForm.tsx index aebd9b1..53ebf03 100644 --- a/src/settings/components/form/PropertiesForm.tsx +++ b/src/settings/components/form/PropertiesForm.tsx @@ -1,6 +1,20 @@ -import "./PropertiesForm.scss"; +import styled from "styled-components"; import React from "react"; +const Form = styled.div``; + +const Row = styled.div``; + +const Label = styled.label` + display: inline-block; + min-width: 5rem; + font-weight: bold; +`; + +const Input = styled.input` + line-height: 2.2rem; +`; + interface Props { types: { [key: string]: string }; value: { [key: string]: any }; @@ -21,7 +35,7 @@ class PropertiesForm extends React.Component { const values = this.props.value; return ( -
+
{Object.keys(types).map((name) => { const type = types[name]; let inputType = ""; @@ -42,23 +56,22 @@ class PropertiesForm extends React.Component { return null; } return ( -
-
+ + ); })} -
+ ); } diff --git a/src/settings/components/form/SearchForm.scss b/src/settings/components/form/SearchForm.scss deleted file mode 100644 index 26b2f44..0000000 --- a/src/settings/components/form/SearchForm.scss +++ /dev/null @@ -1,28 +0,0 @@ -.form-search-form { - @mixin row-base { - display: flex; - - .column-name { - flex: 1; - min-width: 0; - } - .column-url { - flex: 5; - min-width: 0; - } - .column-option { - text-align: right; - flex-basis: 5rem; - } - } - - &-header { - @include row-base; - - font-weight: bold; - } - - &-row { - @include row-base; - } -} diff --git a/src/settings/components/form/SearchForm.tsx b/src/settings/components/form/SearchForm.tsx index a4d923d..3ba0299 100644 --- a/src/settings/components/form/SearchForm.tsx +++ b/src/settings/components/form/SearchForm.tsx @@ -1,9 +1,37 @@ -import "./SearchForm.scss"; import React from "react"; +import styled from "styled-components"; import AddButton from "../ui/AddButton"; import DeleteButton from "../ui/DeleteButton"; import { FormSearch } from "../../../shared/SettingData"; +const Grid = styled.div``; + +const GridRow = styled.div` + display: flex; +`; + +const GridCell = styled.div<{ grow?: number }>` + &:nth-child(1) { + flex-grow: 0; + min-width: 10%; + max-width: 10%; + } + + &:nth-child(2) { + flex-grow: 2; + } + + &:nth-child(3) { + flex-grow: 0; + flex-shrink: 1; + } +`; + +const Input = styled.input` + width: 100%; + box-sizing: border-box; +`; + interface Props { value: FormSearch; onChange: (value: FormSearch) => void; @@ -20,57 +48,61 @@ class SearchForm extends React.Component { render() { const value = this.props.value.toJSON(); return ( -
-
-
Name
-
URL
-
Default
-
- {value.engines.map((engine, index) => { - return ( -
- - -
- - -
-
- ); - })} + <> + + + Name + URL + Default + + {value.engines.map((engine, index) => { + return ( + + + + + + + + + + + + + ); + })} + -
+ ); } -- cgit v1.2.3 From e1e7c2d4d86d7aeb40357add27c76a99a18350e7 Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Mon, 21 Sep 2020 21:15:46 +0900 Subject: Introduce styled-components on top of form --- .../components/form/PartialBlacklistForm.tsx | 9 +- src/settings/components/form/SearchForm.tsx | 9 +- src/settings/components/index.tsx | 96 +++++++++++++--------- src/settings/components/site.scss | 21 ----- 4 files changed, 72 insertions(+), 63 deletions(-) delete mode 100644 src/settings/components/site.scss (limited to 'src/settings/components/form') diff --git a/src/settings/components/form/PartialBlacklistForm.tsx b/src/settings/components/form/PartialBlacklistForm.tsx index 4c6bd35..dcdd00c 100644 --- a/src/settings/components/form/PartialBlacklistForm.tsx +++ b/src/settings/components/form/PartialBlacklistForm.tsx @@ -6,6 +6,11 @@ import Blacklist, { BlacklistItem } from "../../../shared/settings/Blacklist"; const Grid = styled.div``; +const GridHeader = styled.div` + display: flex; + font-weight: bold; +`; + const GridRow = styled.div` display: flex; `; @@ -48,10 +53,10 @@ class PartialBlacklistForm extends React.Component { return ( <> - + URL Keys - + {this.props.value.items.map((item, index) => { if (!item.partial) { return null; diff --git a/src/settings/components/form/SearchForm.tsx b/src/settings/components/form/SearchForm.tsx index 3ba0299..cc7061a 100644 --- a/src/settings/components/form/SearchForm.tsx +++ b/src/settings/components/form/SearchForm.tsx @@ -6,6 +6,11 @@ import { FormSearch } from "../../../shared/SettingData"; const Grid = styled.div``; +const GridHeader = styled.div` + display: flex; + font-weight: bold; +`; + const GridRow = styled.div` display: flex; `; @@ -50,11 +55,11 @@ class SearchForm extends React.Component { return ( <> - + Name URL Default - + {value.engines.map((engine, index) => { return ( diff --git a/src/settings/components/index.tsx b/src/settings/components/index.tsx index d204210..2e2ff52 100644 --- a/src/settings/components/index.tsx +++ b/src/settings/components/index.tsx @@ -1,6 +1,6 @@ -import "./site.scss"; import React from "react"; import { connect } from "react-redux"; +import styled from "styled-components"; import TextArea from "./ui/TextArea"; import Radio from "./ui/Radio"; import SearchForm from "./form/SearchForm"; @@ -19,6 +19,28 @@ import { State as AppState } from "../reducers/setting"; import Properties from "../../shared/settings/Properties"; import Blacklist from "../../shared/settings/Blacklist"; +const Container = styled.form` + padding: 2px; + font-family: system-ui; +`; + +const Fieldset = styled.fieldset` + margin: 0; + padding: 0; + border: none; + margin-top: 1rem; + + &:first-of-type { + margin-top: 1rem; + } +`; + +const Legend = styled.legend` + font-size: 1.5rem; + padding: 0.5rem 0; + font-weight: bold; +`; + const DO_YOU_WANT_TO_CONTINUE = "Some settings in JSON can be lost when migrating. " + "Do you want to continue?"; @@ -41,47 +63,47 @@ class SettingsComponent extends React.Component { renderFormFields(form: FormSettings) { return (
-
- Keybindings +
+ Keybindings -
-
- Search Engines +
+
+ Search Engines -
-
- Blacklist +
+
+ Blacklist -
-
- Partial blacklist +
+
+ Partial blacklist -
-
- Properties +
+
+ Properties -
+
); } @@ -111,30 +133,28 @@ class SettingsComponent extends React.Component { fields = this.renderJsonFields(this.props.json!, this.props.error); } return ( -
+

Configure Vim-Vixen

-
- + - - {fields} - -
+ + {fields} + ); } diff --git a/src/settings/components/site.scss b/src/settings/components/site.scss deleted file mode 100644 index e8415e8..0000000 --- a/src/settings/components/site.scss +++ /dev/null @@ -1,21 +0,0 @@ -.vimvixen-settings-form { - padding: 2px; - font-family: system-ui; - - fieldset { - margin: 0; - padding: 0; - border: none; - margin-top: 1rem; - - fieldset:first-of-type { - margin-top: 1rem; - } - - legend { - font-size: 1.5rem; - padding: .5rem 0; - font-weight: bold; - } - } -} -- cgit v1.2.3 From 1f014295a54f1e16b8bd94da729fb0afd143f7fe Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Mon, 21 Sep 2020 23:27:44 +0900 Subject: Fix component test --- src/settings/components/form/BlacklistForm.tsx | 4 ++-- src/settings/components/ui/TextArea.tsx | 4 +++- test/settings/components/form/BlacklistForm.test.tsx | 20 ++++++++++---------- .../components/form/SearchEngineForm.test.tsx | 10 +++++++--- test/settings/components/ui/TextArea.test.tsx | 2 +- 5 files changed, 23 insertions(+), 17 deletions(-) (limited to 'src/settings/components/form') diff --git a/src/settings/components/form/BlacklistForm.tsx b/src/settings/components/form/BlacklistForm.tsx index 4d794f4..d301f2c 100644 --- a/src/settings/components/form/BlacklistForm.tsx +++ b/src/settings/components/form/BlacklistForm.tsx @@ -40,13 +40,13 @@ class BlacklistForm extends React.Component { render() { return ( <> - + {this.props.value.items.map((item, index) => { if (item.partial) { return null; } return ( - + = (props) => { - {hasError ? {props.error} : null} + {hasError ? ( + {props.error} + ) : null} ); }; diff --git a/test/settings/components/form/BlacklistForm.test.tsx b/test/settings/components/form/BlacklistForm.test.tsx index e34802a..8727c59 100644 --- a/test/settings/components/form/BlacklistForm.test.tsx +++ b/test/settings/components/form/BlacklistForm.test.tsx @@ -17,16 +17,16 @@ describe("settings/form/BlacklistForm", () => { /> ).root; - const rows = root.findAllByProps({ - className: "form-blacklist-form-row", - }); + const rows = root + .findAllByType("div") + .filter((instance) => instance.props.role === "listitem"); expect(rows).to.have.lengthOf(2); - expect( - rows[0].findByProps({ className: "column-url" }).props.value - ).to.equal("*.slack.com"); - expect( - rows[1].findByProps({ className: "column-url" }).props.value - ).to.equal("www.google.com/maps"); + expect(rows[0].findByProps({ name: "url" }).props.value).to.equal( + "*.slack.com" + ); + expect(rows[1].findByProps({ name: "url" }).props.value).to.equal( + "www.google.com/maps" + ); expect(() => root.findByType(AddButton)).not.throw(); }); @@ -113,7 +113,7 @@ describe("settings/form/BlacklistForm", () => { }); const button = document.querySelector( - "input[type=button].ui-add-button" + "input[type=button][name=add]" ) as HTMLButtonElement; ReactTestUtils.Simulate.click(button); }); diff --git a/test/settings/components/form/SearchEngineForm.test.tsx b/test/settings/components/form/SearchEngineForm.test.tsx index 5f835cc..7b10274 100644 --- a/test/settings/components/form/SearchEngineForm.test.tsx +++ b/test/settings/components/form/SearchEngineForm.test.tsx @@ -21,12 +21,16 @@ describe("settings/form/SearchForm", () => { /> ).root; - const names = root.findAllByProps({ name: "name" }); + const names = root + .findAllByType("input") + .filter((instance) => instance.props.name === "name"); expect(names).to.have.lengthOf(2); expect(names[0].props.value).to.equal("google"); expect(names[1].props.value).to.equal("yahoo"); - const urls = root.findAllByProps({ name: "url" }); + const urls = root + .findAllByType("input") + .filter((instance) => instance.props.name === "url"); expect(urls).to.have.lengthOf(2); expect(urls[0].props.value).to.equal("google.com"); expect(urls[1].props.value).to.equal("yahoo.com"); @@ -139,7 +143,7 @@ describe("settings/form/SearchForm", () => { }); const button = document.querySelector( - "input[type=button].ui-add-button" + "input[type=button][name=add]" ) as HTMLInputElement; ReactTestUtils.Simulate.click(button); }); diff --git a/test/settings/components/ui/TextArea.test.tsx b/test/settings/components/ui/TextArea.test.tsx index 50313cf..232c7c0 100644 --- a/test/settings/components/ui/TextArea.test.tsx +++ b/test/settings/components/ui/TextArea.test.tsx @@ -32,7 +32,7 @@ describe("settings/ui/TextArea", () => { const label = document.querySelector("label")!; const textarea = document.querySelector("textarea")!; - const error = document.querySelector(".settings-ui-input-error")!; + const error = document.querySelector("[role=alert]")!; expect(label.textContent).to.contain("myfield"); expect(textarea.nodeName).to.contain("TEXTAREA"); expect(textarea.name).to.contain("myname"); -- cgit v1.2.3 From 738a699259345e47a81cba8d14beb5b9fd2d8b53 Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Tue, 22 Sep 2020 10:52:13 +0900 Subject: Fix e2e test on option --- e2e/lib/FormOptionPage.ts | 177 +++++++++++---------- e2e/lib/JSONOptionPage.ts | 4 +- src/settings/components/form/BlacklistForm.tsx | 3 + .../components/form/PartialBlacklistForm.tsx | 8 +- src/settings/components/form/SearchForm.tsx | 10 +- 5 files changed, 107 insertions(+), 95 deletions(-) (limited to 'src/settings/components/form') diff --git a/e2e/lib/FormOptionPage.ts b/e2e/lib/FormOptionPage.ts index 5551684..666bac7 100644 --- a/e2e/lib/FormOptionPage.ts +++ b/e2e/lib/FormOptionPage.ts @@ -1,5 +1,5 @@ import { Lanthan } from "lanthan"; -import { WebDriver, By, until } from "selenium-webdriver"; +import { WebDriver, WebElement, By, error } from "selenium-webdriver"; export default class FormOptionPage { private webdriver: WebDriver; @@ -9,15 +9,15 @@ export default class FormOptionPage { } async setBlacklist(nth: number, url: string): Promise { - const selector = ".form-blacklist-form-row > .column-url"; - const inputs = await this.webdriver.findElements(By.css(selector)); - if (inputs.length <= nth) { + const fieldset = await this.getFieldsetByLegend("Blacklist"); + const rows = await fieldset.findElements(By.css("[role=listitem]")); + if (rows.length <= nth) { throw new RangeError("Index out of range to set a blacklist"); } - await inputs[nth].sendKeys(url); - await this.webdriver.executeScript( - `document.querySelectorAll('${selector}')[${nth}].blur()` - ); + + const input = rows[nth].findElement(By.css("[aria-label=URL]")); + await input.sendKeys(url); + await this.blurActiveElement(); } async setPartialBlacklist( @@ -25,121 +25,122 @@ export default class FormOptionPage { url: string, keys: string ): Promise { - let selector = ".form-partial-blacklist-form-row > .column-url"; - let inputs = await this.webdriver.findElements(By.css(selector)); - if (inputs.length <= nth) { + const fieldset = await this.getFieldsetByLegend("Partial blacklist"); + const rows = await fieldset.findElements(By.css("[role=listitem]")); + if (rows.length <= nth) { throw new RangeError("Index out of range to set a partial blacklist"); } - await inputs[nth].sendKeys(url); - await this.webdriver.executeScript( - `document.querySelectorAll('${selector}')[${nth}].blur()` - ); - selector = ".form-partial-blacklist-form-row > .column-keys"; - inputs = await this.webdriver.findElements(By.css(selector)); - if (inputs.length <= nth) { - throw new RangeError("Index out of range to set a partial blacklist"); - } - await inputs[nth].sendKeys(keys); - await this.webdriver.executeScript( - `document.querySelectorAll('${selector}')[${nth}].blur()` - ); + const urlInput = rows[nth].findElement(By.css("[aria-label=URL]")); + await urlInput.sendKeys(url); + await this.blurActiveElement(); + + const keysInput = rows[nth].findElement(By.css("[aria-label=Keys]")); + await keysInput.sendKeys(keys); + await this.blurActiveElement(); } async setSearchEngine(nth: number, name: string, url: string) { - let selector = ".form-search-form-row > .column-name"; - let inputs = await this.webdriver.findElements(By.css(selector)); - if (inputs.length <= nth) { + const fieldset = await this.getFieldsetByLegend("Search Engines"); + const rows = await fieldset.findElements(By.css("[role=listitem]")); + if (rows.length <= nth) { throw new RangeError("Index out of range to set a search engine"); } - await inputs[nth].sendKeys(name); - await this.webdriver.executeScript( - `document.querySelectorAll('${selector}')[${nth}].blur()` - ); - selector = ".form-search-form-row > .column-url"; - inputs = await this.webdriver.findElements(By.css(selector)); - if (inputs.length <= nth) { - throw new RangeError("Index out of range to set a search engine"); - } - await inputs[nth].sendKeys(url); - await this.webdriver.executeScript( - `document.querySelectorAll('${selector}')[${nth}].blur()` - ); + const nameInput = rows[nth].findElement(By.css("[aria-label=Name")); + await nameInput.sendKeys(name); + await this.blurActiveElement(); + + const urlInput = rows[nth].findElement(By.css("[aria-label=URL]")); + await urlInput.sendKeys(url); + await this.blurActiveElement(); } async addBlacklist(): Promise { - const rows = await this.webdriver.findElements( - By.css(`.form-blacklist-form-row`) - ); - const button = await this.webdriver.findElement( - By.css(".form-blacklist-form .ui-add-button") - ); - await button.click(); - await this.webdriver.wait( - until.elementLocated( - By.css(`.form-blacklist-form-row:nth-child(${rows.length + 1})`) - ) - ); + const fieldset = await this.getFieldsetByLegend("Blacklist"); + const rows = await fieldset.findElements(By.css("[role=listitem]")); + const addButton = await fieldset.findElement(By.css("[aria-label=Add]")); + + await addButton.click(); + await this.webdriver.wait(async () => { + const newRows = await fieldset.findElements(By.css("[role=listitem]")); + return newRows.length == rows.length + 1; + }); } async addPartialBlacklist(): Promise { - const rows = await this.webdriver.findElements( - By.css(`.form-partial-blacklist-form-row`) - ); - const button = await this.webdriver.findElement( - By.css(".form-partial-blacklist-form .ui-add-button") - ); - await button.click(); - await this.webdriver.wait( - until.elementLocated( - By.css(`.form-partial-blacklist-form-row:nth-child(${rows.length + 2})`) - ) - ); + const fieldset = await this.getFieldsetByLegend("Partial blacklist"); + const addButton = await fieldset.findElement(By.css("[aria-label=Add]")); + const rows = await fieldset.findElements(By.css("[role=listitem]")); + + await addButton.click(); + await this.webdriver.wait(async () => { + const newRows = await fieldset.findElements(By.css("[role=listitem]")); + return newRows.length == rows.length + 1; + }); } async removeBlackList(nth: number): Promise { - const buttons = await this.webdriver.findElements( - By.css(".form-blacklist-form-row .ui-delete-button") + const fieldset = await this.getFieldsetByLegend("Blacklist"); + const deleteButtons = await fieldset.findElements( + By.css("[aria-label=Delete]") ); - if (buttons.length <= nth) { + if (deleteButtons.length <= nth) { throw new RangeError("Index out of range to remove blacklist"); } - await buttons[nth].click(); + await deleteButtons[nth].click(); } async removePartialBlackList(nth: number): Promise { - const buttons = await this.webdriver.findElements( - By.css(".form-partial-blacklist-form-row .ui-delete-button") - ); - if (buttons.length <= nth) { - throw new RangeError("Index out of range to remove partial blacklist"); + const fieldset = await this.getFieldsetByLegend("Partial blacklist"); + const deleteButtons = await fieldset.findElements( + By.css("[aria-label=Delete]") + ); + if (deleteButtons.length <= nth) { + throw new RangeError( + `Index out of range ${deleteButtons.length} to remove partial blacklist ${nth}` + ); } - await buttons[nth].click(); + await deleteButtons[nth].click(); } async addSearchEngine(): Promise { - const rows = await this.webdriver.findElements( - By.css(`.form-search-form-row > .column-name`) - ); - const button = await this.webdriver.findElement( - By.css(".form-search-form > .ui-add-button") - ); - await button.click(); - await this.webdriver.wait( - until.elementLocated( - By.css(`.form-search-form-row:nth-child(${rows.length + 1})`) - ) - ); + const fieldset = await this.getFieldsetByLegend("Search Engines"); + const rows = await fieldset.findElements(By.css("[role=listitem]")); + const addButton = await fieldset.findElement(By.css("[aria-label=Add]")); + + await addButton.click(); + await this.webdriver.wait(async () => { + const newRows = await fieldset.findElements(By.css("[role=listitem]")); + return newRows.length == rows.length + 1; + }); } async setDefaultSearchEngine(nth: number): Promise { - const radios = await this.webdriver.findElements( - By.css(".form-search-form-row input[type=radio]") + const fieldset = await this.getFieldsetByLegend("Search Engines"); + const radios = await fieldset.findElements( + By.css("[name=default][type=radio]") ); if (radios.length <= nth) { throw new RangeError("Index out of range to set a default search engine"); } await radios[nth].click(); } + + private async getFieldsetByLegend(legendText: string): Promise { + const fieldsets = await this.webdriver.findElements(By.tagName("fieldset")); + for (const fieldset of fieldsets) { + const legend = await fieldset.findElement(By.tagName("legend")); + if ((await legend.getText()) === legendText) { + return fieldset; + } + } + throw new error.NoSuchElementError( + `Unable to locate fieldset with legend: ` + legendText + ); + } + + private async blurActiveElement(): Promise { + await this.webdriver.executeScript(`document.activeElement.blur()`); + } } diff --git a/e2e/lib/JSONOptionPage.ts b/e2e/lib/JSONOptionPage.ts index 0f2b0a7..2d8147e 100644 --- a/e2e/lib/JSONOptionPage.ts +++ b/e2e/lib/JSONOptionPage.ts @@ -20,9 +20,7 @@ export default class JSONOptionPage { } async getErrorMessage(): Promise { - const error = await this.webdriver.findElement( - By.css(".settings-ui-input-error") - ); + const error = await this.webdriver.findElement(By.css("p[role=alert]")); return error.getText(); } } diff --git a/src/settings/components/form/BlacklistForm.tsx b/src/settings/components/form/BlacklistForm.tsx index d301f2c..6fb9eca 100644 --- a/src/settings/components/form/BlacklistForm.tsx +++ b/src/settings/components/form/BlacklistForm.tsx @@ -52,6 +52,7 @@ class BlacklistForm extends React.Component { data-index={index} type="text" name="url" + aria-label="URL" value={item.pattern} placeholder="example.com/mail/*" onChange={this.bindValue.bind(this)} @@ -64,6 +65,7 @@ class BlacklistForm extends React.Component { name="delete" onClick={this.bindValue.bind(this)} onBlur={this.props.onBlur} + aria-label="Delete" /> @@ -72,6 +74,7 @@ class BlacklistForm extends React.Component { diff --git a/src/settings/components/form/PartialBlacklistForm.tsx b/src/settings/components/form/PartialBlacklistForm.tsx index dcdd00c..b2da2bb 100644 --- a/src/settings/components/form/PartialBlacklistForm.tsx +++ b/src/settings/components/form/PartialBlacklistForm.tsx @@ -52,7 +52,7 @@ class PartialBlacklistForm extends React.Component { render() { return ( <> - + URL Keys @@ -62,12 +62,13 @@ class PartialBlacklistForm extends React.Component { return null; } return ( - + { data-index={index} type="text" name="keys" + aria-label="Keys" value={item.keys.join(",")} placeholder="j,k,," onChange={this.bindValue.bind(this)} @@ -89,6 +91,7 @@ class PartialBlacklistForm extends React.Component { @@ -99,6 +102,7 @@ class PartialBlacklistForm extends React.Component { diff --git a/src/settings/components/form/SearchForm.tsx b/src/settings/components/form/SearchForm.tsx index cc7061a..4bf0e02 100644 --- a/src/settings/components/form/SearchForm.tsx +++ b/src/settings/components/form/SearchForm.tsx @@ -54,7 +54,7 @@ class SearchForm extends React.Component { const value = this.props.value.toJSON(); return ( <> - + Name URL @@ -62,12 +62,13 @@ class SearchForm extends React.Component { {value.engines.map((engine, index) => { return ( - + { data-index={index} type="text" name="url" + aria-label="URL" placeholder="http://example.com/?q={}" value={engine[1]} onChange={this.bindValue.bind(this)} @@ -89,11 +91,14 @@ class SearchForm extends React.Component { data-index={index} type="radio" name="default" + aria-label="Default" checked={value.default === engine[0]} onChange={this.bindValue.bind(this)} /> + a @@ -104,6 +109,7 @@ class SearchForm extends React.Component { -- cgit v1.2.3