Commit 447bd418 authored by Jesse Schettler's avatar Jesse Schettler Committed by Commit Bot

scanning: Add dropdown for scan sources

Add a new Polymer element, source-select, to display scan sources (e.g.
flatbed, document feeder, etc.) in a dropdown. Styling will be added in
subsequent CLs.

Bug: 1059779
Change-Id: I88603298c3c749905527b5d74216f6285d984d51
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2424988Reviewed-by: default avatarKyle Horimoto <khorimoto@chromium.org>
Reviewed-by: default avatarJimmy Gong <jimmyxgong@chromium.org>
Commit-Queue: Jesse Schettler <jschettler@chromium.org>
Cr-Commit-Position: refs/heads/master@{#811407}
parent 646caf2a
...@@ -9,7 +9,13 @@ import 'chrome://scanning/scanning_app.js'; ...@@ -9,7 +9,13 @@ import 'chrome://scanning/scanning_app.js';
import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {setScanServiceForTesting} from 'chrome://scanning/mojo_interface_provider.js'; import {setScanServiceForTesting} from 'chrome://scanning/mojo_interface_provider.js';
import {ScannerArr} from 'chrome://scanning/scanning_app_types.js'; import {ScannerArr} from 'chrome://scanning/scanning_app_types.js';
import {tokenToString} from 'chrome://scanning/scanning_app_util.js'; import {getSourceTypeString, tokenToString} from 'chrome://scanning/scanning_app_util.js';
const SourceType = {
FLATBED: chromeos.scanning.mojom.SourceType.kFlatbed,
ADF_SIMPLEX: chromeos.scanning.mojom.SourceType.kAdfSimplex,
ADF_DUPLEX: chromeos.scanning.mojom.SourceType.kAdfDuplex,
};
/** /**
* @param {!mojoBase.mojom.UnguessableToken} id * @param {!mojoBase.mojom.UnguessableToken} id
...@@ -24,6 +30,19 @@ function createScanner(id, displayName) { ...@@ -24,6 +30,19 @@ function createScanner(id, displayName) {
return scanner; return scanner;
} }
/**
* @param {number} type
* @param {!string} name
* @return {!chromeos.scanning.mojom.ScanSource}
*/
function createSource(type, name) {
let source = {
'type': type,
'name': name,
};
return source;
}
/** /**
* Converts a JS string to a mojo_base::mojom::String16 object. * Converts a JS string to a mojo_base::mojom::String16 object.
* @param {!string} str * @param {!string} str
...@@ -217,3 +236,66 @@ suite('ScannerSelectTest', () => { ...@@ -217,3 +236,66 @@ suite('ScannerSelectTest', () => {
assertTrue(select.disabled); assertTrue(select.disabled);
}); });
}); });
suite('SourceSelectTest', () => {
/** @type {!SourceSelectElement} */
let sourceSelect;
setup(() => {
sourceSelect = document.createElement('source-select');
assertTrue(!!sourceSelect);
document.body.appendChild(sourceSelect);
});
teardown(() => {
sourceSelect.remove();
sourceSelect = null;
});
test('initializeSourceSelect', () => {
// Before options are added, the dropdown should be disabled.
const select = sourceSelect.$$('select');
assertTrue(!!select);
assertTrue(select.disabled);
const firstSource = createSource(SourceType.FLATBED, 'platen');
const secondSource = createSource(SourceType.ADF_SIMPLEX, 'adf simplex');
const sourceArr = [firstSource, secondSource];
sourceSelect.sources = sourceArr;
flush();
// Verify that adding more than one source results in the dropdown becoming
// enabled with the correct options.
assertFalse(select.disabled);
assertEquals(2, select.length);
assertEquals(
getSourceTypeString(firstSource.type),
select.options[0].textContent.trim());
assertEquals(
getSourceTypeString(secondSource.type),
select.options[1].textContent.trim());
assertEquals(firstSource.name, select.value);
});
test('sourceSelectDisabled', () => {
const select = sourceSelect.$$('select');
assertTrue(!!select);
let sourceArr = [createSource(SourceType.FLATBED, 'flatbed')];
sourceSelect.sources = sourceArr;
flush();
// Verify the dropdown is disabled when there's only one option.
assertEquals(1, select.length);
assertTrue(select.disabled);
sourceArr =
sourceArr.concat([createSource(SourceType.ADF_DUPLEX, 'adf duplex')]);
sourceSelect.sources = sourceArr;
flush();
// Verify the dropdown is enabled when there's more than one option.
assertEquals(2, select.length);
assertFalse(select.disabled);
});
});
...@@ -469,6 +469,9 @@ Try tapping the mic to ask me anything. ...@@ -469,6 +469,9 @@ Try tapping the mic to ask me anything.
<message name="IDS_SCANNING_APP_NO_SCANNERS_TEXT" desc="The text displayed in the scanner dropdown when no scanners are available."> <message name="IDS_SCANNING_APP_NO_SCANNERS_TEXT" desc="The text displayed in the scanner dropdown when no scanners are available.">
No available scanners No available scanners
</message> </message>
<message name="IDS_SCANNING_APP_SOURCE_DROPDOWN_LABEL" desc="The label for the dropdown that displays available scan sources (e.g. flatbed, document feeder, etc.).">
Source
</message>
</messages> </messages>
</release> </release>
</grit> </grit>
10b62b0b733ae021d495c7ea155e5d756493ddcf
\ No newline at end of file
...@@ -15,6 +15,7 @@ js_type_check("closure_compile_module") { ...@@ -15,6 +15,7 @@ js_type_check("closure_compile_module") {
":scanning_app", ":scanning_app",
":scanning_app_types", ":scanning_app_types",
":scanning_app_util", ":scanning_app_util",
":source_select",
] ]
} }
...@@ -34,6 +35,7 @@ js_library("scanning_app") { ...@@ -34,6 +35,7 @@ js_library("scanning_app") {
":scanner_select", ":scanner_select",
":scanning_app_types", ":scanning_app_types",
":scanning_app_util", ":scanning_app_util",
":source_select",
"//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled", "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
] ]
} }
...@@ -44,6 +46,15 @@ js_library("scanning_app_types") { ...@@ -44,6 +46,15 @@ js_library("scanning_app_types") {
js_library("scanning_app_util") { js_library("scanning_app_util") {
} }
js_library("source_select") {
deps = [
":scanning_app_util",
"//chromeos/components/scanning/mojom:mojom_js_library_for_compile",
"//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
"//ui/webui/resources/js:i18n_behavior.m",
]
}
js_library("mojo_interface_provider") { js_library("mojo_interface_provider") {
deps = [ deps = [
"//chromeos/components/scanning/mojom:mojom_js_library_for_compile", "//chromeos/components/scanning/mojom:mojom_js_library_for_compile",
...@@ -55,6 +66,7 @@ html_to_js("web_components") { ...@@ -55,6 +66,7 @@ html_to_js("web_components") {
js_files = [ js_files = [
"scanner_select.js", "scanner_select.js",
"scanning_app.js", "scanning_app.js",
"source_select.js",
"throbber_css.js", "throbber_css.js",
] ]
} }
<div id="header"></div> <div id="header"></div>
<scanner-select scanners="[[scanners_]]" loaded="[[loaded_]]"></scanner-select> <div>
<scanner-select scanners="[[scanners_]]" loaded="[[loaded_]]"></scanner-select>
</div>
<div>
<source-select sources="[[capabilities_.sources]]"></source-select>
</div>
...@@ -6,6 +6,7 @@ import 'chrome://resources/mojo/mojo/public/mojom/base/big_buffer.mojom-lite.js' ...@@ -6,6 +6,7 @@ import 'chrome://resources/mojo/mojo/public/mojom/base/big_buffer.mojom-lite.js'
import 'chrome://resources/mojo/mojo/public/mojom/base/string16.mojom-lite.js'; import 'chrome://resources/mojo/mojo/public/mojom/base/string16.mojom-lite.js';
import 'chrome://resources/mojo/mojo/public/mojom/base/unguessable_token.mojom-lite.js'; import 'chrome://resources/mojo/mojo/public/mojom/base/unguessable_token.mojom-lite.js';
import './scanner_select.js'; import './scanner_select.js';
import './source_select.js';
import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {getScanService} from './mojo_interface_provider.js'; import {getScanService} from './mojo_interface_provider.js';
...@@ -43,6 +44,18 @@ Polymer({ ...@@ -43,6 +44,18 @@ Polymer({
*/ */
selectedScannerId_: Object, selectedScannerId_: Object,
/**
* @type {?chromeos.scanning.mojom.ScannerCapabilities}
* @private
*/
capabilities_: Object,
/**
* @type {?chromeos.scanning.mojom.ScanSource}
* @private
*/
selectedSoure_: Object,
/** @private */ /** @private */
loaded_: { loaded_: {
type: Boolean, type: Boolean,
...@@ -52,6 +65,7 @@ Polymer({ ...@@ -52,6 +65,7 @@ Polymer({
listeners: { listeners: {
'selected-scanner-change': 'onSelectedScannerChange_', 'selected-scanner-change': 'onSelectedScannerChange_',
'selected-source-change': 'onSelectedSourceChange_',
}, },
/** @override */ /** @override */
...@@ -64,6 +78,19 @@ Polymer({ ...@@ -64,6 +78,19 @@ Polymer({
this.scanService_.getScanners().then(this.onScannersReceived_.bind(this)); this.scanService_.getScanners().then(this.onScannersReceived_.bind(this));
}, },
/**
* @param {!{capabilities: !chromeos.scanning.mojom.ScannerCapabilities}}
* response
* @private
*/
onCapabilitiesReceived_(response) {
this.capabilities_ = response.capabilities;
// Set the first source as the selected source since it will be the first
// option in the dropdown.
this.selectedSoure_ = this.capabilities_.sources[0];
},
/** /**
* @param {!{scanners: !ScannerArr}} response * @param {!{scanners: !ScannerArr}} response
* @private * @private
...@@ -83,7 +110,8 @@ Polymer({ ...@@ -83,7 +110,8 @@ Polymer({
// selected ID to the fist scanner's ID until a different scanner is // selected ID to the fist scanner's ID until a different scanner is
// selected. // selected.
this.selectedScannerId_ = this.scanners_[0].id; this.selectedScannerId_ = this.scanners_[0].id;
// TODO(jschettler): Get capabilities for the scanner. this.scanService_.getScannerCapabilities(this.selectedScannerId_)
.then(this.onCapabilitiesReceived_.bind(this));
}, },
/** /**
...@@ -97,6 +125,21 @@ Polymer({ ...@@ -97,6 +125,21 @@ Polymer({
} }
this.selectedScannerId_ = this.scannerIds_.get(value); this.selectedScannerId_ = this.scannerIds_.get(value);
// TODO(jschettler): Get capabilities for the selected scanner. this.scanService_.getScannerCapabilities(this.selectedScannerId_)
.then(this.onCapabilitiesReceived_.bind(this));
},
/**
* @param {!Event} event
* @private
*/
onSelectedSourceChange_(event) {
const value = event.detail.value;
for (const source of this.capabilities_.sources) {
if (source.name === value) {
this.selectedSoure_ = source;
break;
}
}
}, },
}); });
...@@ -18,6 +18,8 @@ ...@@ -18,6 +18,8 @@
<include name="IDR_SCANNING_APP_JS" file="${root_gen_dir}/chromeos/components/scanning/resources/scanning_app.js" use_base_dir="false" compress="gzip" type="BINDATA"/> <include name="IDR_SCANNING_APP_JS" file="${root_gen_dir}/chromeos/components/scanning/resources/scanning_app.js" use_base_dir="false" compress="gzip" type="BINDATA"/>
<include name="IDR_SCANNING_APP_SCANNER_SELECT_HTML" file="scanner_select.html" compress="gzip" type="BINDATA"/> <include name="IDR_SCANNING_APP_SCANNER_SELECT_HTML" file="scanner_select.html" compress="gzip" type="BINDATA"/>
<include name="IDR_SCANNING_APP_SCANNER_SELECT_JS" file="${root_gen_dir}/chromeos/components/scanning/resources/scanner_select.js" use_base_dir="false" compress="gzip" type="BINDATA"/> <include name="IDR_SCANNING_APP_SCANNER_SELECT_JS" file="${root_gen_dir}/chromeos/components/scanning/resources/scanner_select.js" use_base_dir="false" compress="gzip" type="BINDATA"/>
<include name="IDR_SCANNING_APP_SOURCE_SELECT_HTML" file="source_select.html" compress="gzip" type="BINDATA"/>
<include name="IDR_SCANNING_APP_SOURCE_SELECT_JS" file="${root_gen_dir}/chromeos/components/scanning/resources/source_select.js" use_base_dir="false" compress="gzip" type="BINDATA"/>
<include name="IDR_SCANNING_APP_THROBBER_CSS_JS" file="${root_gen_dir}/chromeos/components/scanning/resources/throbber_css.js" use_base_dir="false" type="BINDATA"/> <include name="IDR_SCANNING_APP_THROBBER_CSS_JS" file="${root_gen_dir}/chromeos/components/scanning/resources/throbber_css.js" use_base_dir="false" type="BINDATA"/>
<include name="IDR_SCANNING_APP_ICON" file="app_icon_192.png" type="BINDATA" /> <include name="IDR_SCANNING_APP_ICON" file="app_icon_192.png" type="BINDATA" />
</includes> </includes>
......
...@@ -2,6 +2,29 @@ ...@@ -2,6 +2,29 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import {assertNotReached} from 'chrome://resources/js/assert.m.js';
/**
* Converts a chromeos.scanning.mojom.SourceType to a string that can be
* displayed in the source dropdown.
* @param {number} mojoSourceType
* @return {!string}
*/
export function getSourceTypeString(mojoSourceType) {
// TODO(jschettler): Replace with finalized i18n strings.
switch (mojoSourceType) {
case chromeos.scanning.mojom.SourceType.kFlatbed:
return 'Flatbed';
case chromeos.scanning.mojom.SourceType.kAdfSimplex:
return 'Document Feeder (Simplex)';
case chromeos.scanning.mojom.SourceType.kAdfDuplex:
return 'Document Feeder (Duplex)';
default:
assertNotReached();
return 'Unknown';
}
}
/** /**
* Converts an unguessable token to a string by combining the high and low * Converts an unguessable token to a string by combining the high and low
* values as strings with a hashtag as the separator. * values as strings with a hashtag as the separator.
......
<style>
#title {
height: 32px;
padding-inline-end: 10px;
}
#controls {
display: inline-block;
height: 32px;
width: 300px;
}
</style>
<span id="title">[[i18n('sourceDropdownLabel')]]</span>
<div id="controls">
<!-- TODO(jschettler): Verify this meets a11y expecations (e.g. ChromeVox
should announce when a new option is focused). -->
<select class="md-select" on-change="onSelectedSourceChange_"
disabled="[[disabled_]]">
<!-- TODO(jschettler): Figure out why hiding/disabling the option doesn't
remove it from the dropdown. -->
<template is="dom-if" if="[[!sources.length]]" restamp>
<!-- TODO(jschettler): Replace default option with finalized i18n
string. -->
<option value="">No sources</option>
</template>
<!-- TODO(jschettler): Determine how the sources should be sorted. -->
<template is="dom-repeat" items="[[sources]]" as="source">
<option value="[[source.name]]">
[[getSourceTypeString_(source.type)]]
</option>
</template>
</select>
</div>
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'chrome://resources/mojo/mojo/public/mojom/base/big_buffer.mojom-lite.js';
import 'chrome://resources/mojo/mojo/public/mojom/base/string16.mojom-lite.js';
import 'chrome://resources/mojo/mojo/public/mojom/base/unguessable_token.mojom-lite.js';
import './scanning.mojom-lite.js';
import {getSourceTypeString} from './scanning_app_util.js';
import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js';
import './strings.js';
/** @type {number} */
const NUM_REQUIRED_SOURCES = 2;
/**
* @fileoverview
* 'source-select' displays the available scanner sources in a dropdown.
*/
Polymer({
is: 'source-select',
_template: html`{__html_template__}`,
behaviors: [I18nBehavior],
properties: {
/** @type {!Array<!chromeos.scanning.mojom.ScanSource>} */
sources: {
type: Array,
value: () => [],
},
/** @private */
disabled_: Boolean,
},
observers: [
'updateDisabled_(sources.length)',
],
/**
* @param {number} mojoSourceType
* @return {!string}
* @private
*/
getSourceTypeString_(mojoSourceType) {
return getSourceTypeString(mojoSourceType);
},
/**
* @param {!Event} event
* @private
*/
onSelectedSourceChange_(event) {
this.fire('selected-source-change', event.target);
},
/**
* Disables the dropdown based on the number of available sources.
* @param {number} numSources
* @private
*/
updateDisabled_(numSources) {
this.disabled_ = numSources < NUM_REQUIRED_SOURCES;
},
});
...@@ -50,7 +50,8 @@ void AddScanningAppStrings(content::WebUIDataSource* html_source) { ...@@ -50,7 +50,8 @@ void AddScanningAppStrings(content::WebUIDataSource* html_source) {
static constexpr webui::LocalizedString kLocalizedStrings[] = { static constexpr webui::LocalizedString kLocalizedStrings[] = {
{"appTitle", IDS_SCANNING_APP_TITLE}, {"appTitle", IDS_SCANNING_APP_TITLE},
{"scannerDropdownLabel", IDS_SCANNING_APP_SCANNER_DROPDOWN_LABEL}, {"scannerDropdownLabel", IDS_SCANNING_APP_SCANNER_DROPDOWN_LABEL},
{"noScannersText", IDS_SCANNING_APP_NO_SCANNERS_TEXT}}; {"noScannersText", IDS_SCANNING_APP_NO_SCANNERS_TEXT},
{"sourceDropdownLabel", IDS_SCANNING_APP_SOURCE_DROPDOWN_LABEL}};
for (const auto& str : kLocalizedStrings) for (const auto& str : kLocalizedStrings)
html_source->AddLocalizedString(str.name, str.id); html_source->AddLocalizedString(str.name, str.id);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment