summaryrefslogtreecommitdiffstats
path: root/devtools/client/accessibility/components/AccessibilityTreeFilter.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/accessibility/components/AccessibilityTreeFilter.js')
-rw-r--r--devtools/client/accessibility/components/AccessibilityTreeFilter.js171
1 files changed, 171 insertions, 0 deletions
diff --git a/devtools/client/accessibility/components/AccessibilityTreeFilter.js b/devtools/client/accessibility/components/AccessibilityTreeFilter.js
new file mode 100644
index 0000000000..90eabd350a
--- /dev/null
+++ b/devtools/client/accessibility/components/AccessibilityTreeFilter.js
@@ -0,0 +1,171 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+/* global gTelemetry */
+
+// React
+const {
+ createFactory,
+ Component,
+} = require("resource://devtools/client/shared/vendor/react.js");
+const {
+ div,
+ hr,
+ span,
+} = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
+const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
+const {
+ L10N,
+} = require("resource://devtools/client/accessibility/utils/l10n.js");
+
+loader.lazyGetter(this, "MenuButton", function () {
+ return createFactory(
+ require("resource://devtools/client/shared/components/menu/MenuButton.js")
+ );
+});
+loader.lazyGetter(this, "MenuItem", function () {
+ return createFactory(
+ require("resource://devtools/client/shared/components/menu/MenuItem.js")
+ );
+});
+loader.lazyGetter(this, "MenuList", function () {
+ return createFactory(
+ require("resource://devtools/client/shared/components/menu/MenuList.js")
+ );
+});
+
+const actions = require("resource://devtools/client/accessibility/actions/audit.js");
+
+const {
+ connect,
+} = require("resource://devtools/client/shared/vendor/react-redux.js");
+const {
+ FILTERS,
+} = require("resource://devtools/client/accessibility/constants.js");
+
+const TELEMETRY_AUDIT_ACTIVATED = "devtools.accessibility.audit_activated";
+const FILTER_LABELS = {
+ [FILTERS.NONE]: "accessibility.filter.none",
+ [FILTERS.ALL]: "accessibility.filter.all2",
+ [FILTERS.CONTRAST]: "accessibility.filter.contrast",
+ [FILTERS.KEYBOARD]: "accessibility.filter.keyboard",
+ [FILTERS.TEXT_LABEL]: "accessibility.filter.textLabel",
+};
+
+class AccessibilityTreeFilter extends Component {
+ static get propTypes() {
+ return {
+ auditing: PropTypes.array.isRequired,
+ filters: PropTypes.object.isRequired,
+ dispatch: PropTypes.func.isRequired,
+ describedby: PropTypes.string,
+ toolboxDoc: PropTypes.object.isRequired,
+ audit: PropTypes.func.isRequired,
+ };
+ }
+
+ async toggleFilter(filterKey) {
+ const { audit: auditFunc, dispatch, filters } = this.props;
+
+ if (filterKey !== FILTERS.NONE && !filters[filterKey]) {
+ if (gTelemetry) {
+ gTelemetry.keyedScalarAdd(TELEMETRY_AUDIT_ACTIVATED, filterKey, 1);
+ }
+
+ dispatch(actions.auditing(filterKey));
+ await dispatch(actions.audit(auditFunc, filterKey));
+ }
+
+ // We wait to dispatch filter toggle until the tree is ready to be filtered
+ // right after the audit. This is to make sure that we render an empty tree
+ // (filtered) while the audit is running.
+ dispatch(actions.filterToggle(filterKey));
+ }
+
+ onClick(filterKey) {
+ this.toggleFilter(filterKey);
+ }
+
+ render() {
+ const { auditing, filters, describedby, toolboxDoc } = this.props;
+ const toolbarLabelID = "accessibility-tree-filters-label";
+ const filterNoneChecked = !Object.values(filters).includes(true);
+ const items = [
+ MenuItem({
+ key: FILTERS.NONE,
+ checked: filterNoneChecked,
+ className: `filter ${FILTERS.NONE}`,
+ label: L10N.getStr(FILTER_LABELS[FILTERS.NONE]),
+ onClick: this.onClick.bind(this, FILTERS.NONE),
+ disabled: !!auditing.length,
+ }),
+ hr({ key: "hr-1" }),
+ ];
+
+ const { [FILTERS.ALL]: filterAllChecked, ...filtersWithoutAll } = filters;
+ items.push(
+ MenuItem({
+ key: FILTERS.ALL,
+ checked: filterAllChecked,
+ className: `filter ${FILTERS.ALL}`,
+ label: L10N.getStr(FILTER_LABELS[FILTERS.ALL]),
+ onClick: this.onClick.bind(this, FILTERS.ALL),
+ disabled: !!auditing.length,
+ }),
+ hr({ key: "hr-2" }),
+ Object.entries(filtersWithoutAll).map(([filterKey, active]) =>
+ MenuItem({
+ key: filterKey,
+ checked: active,
+ className: `filter ${filterKey}`,
+ label: L10N.getStr(FILTER_LABELS[filterKey]),
+ onClick: this.onClick.bind(this, filterKey),
+ disabled: !!auditing.length,
+ })
+ )
+ );
+
+ let label;
+ if (filterNoneChecked) {
+ label = L10N.getStr(FILTER_LABELS[FILTERS.NONE]);
+ } else if (filterAllChecked) {
+ label = L10N.getStr(FILTER_LABELS[FILTERS.ALL]);
+ } else {
+ label = Object.keys(filtersWithoutAll)
+ .filter(filterKey => filtersWithoutAll[filterKey])
+ .map(filterKey => L10N.getStr(FILTER_LABELS[filterKey]))
+ .join(", ");
+ }
+
+ return div(
+ {
+ role: "group",
+ className: "accessibility-tree-filters",
+ "aria-labelledby": toolbarLabelID,
+ "aria-describedby": describedby,
+ },
+ span(
+ { id: toolbarLabelID, role: "presentation" },
+ L10N.getStr("accessibility.tree.filters")
+ ),
+ MenuButton(
+ {
+ menuId: "accessibility-tree-filters-menu",
+ toolboxDoc,
+ className: `devtools-button badge toolbar-menu-button filters`,
+ label,
+ },
+ MenuList({}, items)
+ )
+ );
+ }
+}
+
+const mapStateToProps = ({ audit: { filters, auditing } }) => {
+ return { filters, auditing };
+};
+
+// Exports from this module
+module.exports = connect(mapStateToProps)(AccessibilityTreeFilter);