mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 21:01:08 +00:00
Bug 1550001 - Provide event listener breakpoint UI r=loganfsmyth
Differential Revision: https://phabricator.services.mozilla.com/D30986 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
eef685d1fd
commit
bc1c818a3b
@ -4,29 +4,88 @@
|
||||
|
||||
// @flow
|
||||
|
||||
import { uniq, remove } from "lodash";
|
||||
|
||||
import { asyncStore } from "../utils/prefs";
|
||||
|
||||
import {
|
||||
getActiveEventListeners,
|
||||
getEventListenerExpanded,
|
||||
} from "../selectors";
|
||||
|
||||
import type { ThunkArgs } from "./types";
|
||||
import type { EventListenerBreakpoints } from "../types";
|
||||
|
||||
export function addEventListeners(events: EventListenerBreakpoints) {
|
||||
return async ({ dispatch, client }: ThunkArgs) => {
|
||||
await dispatch({
|
||||
type: "ADD_EVENT_LISTENERS",
|
||||
events,
|
||||
});
|
||||
const newList = await asyncStore.eventListenerBreakpoints;
|
||||
client.setEventListenerBreakpoints(newList);
|
||||
async function updateBreakpoints(dispatch, client, newEvents: string[]) {
|
||||
dispatch({ type: "UPDATE_EVENT_LISTENERS", active: newEvents });
|
||||
|
||||
const current = await asyncStore.eventListenerBreakpoints;
|
||||
asyncStore.eventListenerBreakpoints = {
|
||||
...current,
|
||||
active: newEvents,
|
||||
};
|
||||
|
||||
client.setEventListenerBreakpoints(newEvents);
|
||||
}
|
||||
|
||||
async function updateExpanded(dispatch, newExpanded: string[]) {
|
||||
dispatch({
|
||||
type: "UPDATE_EVENT_LISTENER_EXPANDED",
|
||||
expanded: newExpanded,
|
||||
});
|
||||
|
||||
const current = await asyncStore.eventListenerBreakpoints;
|
||||
asyncStore.eventListenerBreakpoints = {
|
||||
...current,
|
||||
expanded: newExpanded,
|
||||
};
|
||||
}
|
||||
|
||||
export function removeEventListeners(events: EventListenerBreakpoints) {
|
||||
return async ({ dispatch, client }: ThunkArgs) => {
|
||||
await dispatch({
|
||||
type: "REMOVE_EVENT_LISTENERS",
|
||||
events,
|
||||
});
|
||||
const newList = await asyncStore.eventListenerBreakpoints;
|
||||
client.setEventListenerBreakpoints(newList);
|
||||
export function addEventListenerBreakpoints(eventsToAdd: string[]) {
|
||||
return async ({ dispatch, client, getState }: ThunkArgs) => {
|
||||
const activeListenerBreakpoints = await getActiveEventListeners(getState());
|
||||
|
||||
const newEvents = uniq([...eventsToAdd, ...activeListenerBreakpoints]);
|
||||
|
||||
updateBreakpoints(dispatch, client, newEvents);
|
||||
};
|
||||
}
|
||||
|
||||
export function removeEventListenerBreakpoints(eventsToRemove: string[]) {
|
||||
return async ({ dispatch, client, getState }: ThunkArgs) => {
|
||||
const activeListenerBreakpoints = await getActiveEventListeners(getState());
|
||||
|
||||
const newEvents = remove(
|
||||
activeListenerBreakpoints,
|
||||
event => !eventsToRemove.includes(event)
|
||||
);
|
||||
|
||||
updateBreakpoints(dispatch, client, newEvents);
|
||||
};
|
||||
}
|
||||
|
||||
export function addEventListenerExpanded(category: string) {
|
||||
return async ({ dispatch, getState }: ThunkArgs) => {
|
||||
const expanded = await getEventListenerExpanded(getState());
|
||||
|
||||
const newExpanded = uniq([...expanded, category]);
|
||||
|
||||
await updateExpanded(dispatch, newExpanded);
|
||||
};
|
||||
}
|
||||
|
||||
export function removeEventListenerExpanded(category: string) {
|
||||
return async ({ dispatch, getState }: ThunkArgs) => {
|
||||
const expanded = await getEventListenerExpanded(getState());
|
||||
|
||||
const newExpanded = expanded.filter(expand => expand != category);
|
||||
|
||||
updateExpanded(dispatch, newExpanded);
|
||||
};
|
||||
}
|
||||
|
||||
export function getEventListenerBreakpointTypes() {
|
||||
return async ({ dispatch, client }: ThunkArgs) => {
|
||||
const categories = await client.getEventListenerBreakpointTypes();
|
||||
dispatch({ type: "RECEIVE_EVENT_LISTENER_TYPES", categories });
|
||||
};
|
||||
}
|
||||
|
@ -159,6 +159,30 @@ export type {
|
||||
|
||||
export type { panelPositionType } from "./UIAction";
|
||||
|
||||
export type { ASTAction } from "./ASTAction";
|
||||
|
||||
type ActiveEventListener = string;
|
||||
type EventListenerEvent = { name: string, id: ActiveEventListener };
|
||||
type EventListenerCategory = { name: string, events: EventListenerEvent[] };
|
||||
|
||||
export type EventListenerActiveList = ActiveEventListener[];
|
||||
export type EventListenerCategoryList = EventListenerCategory[];
|
||||
export type EventListenerExpandedList = string[];
|
||||
|
||||
export type EventListenerAction =
|
||||
| {|
|
||||
+type: "UPDATE_EVENT_LISTENERS",
|
||||
+active: EventListenerActiveList,
|
||||
|}
|
||||
| {|
|
||||
+type: "RECEIVE_EVENT_LISTENER_TYPES",
|
||||
+categories: EventListenerCategoryList,
|
||||
|}
|
||||
| {|
|
||||
+type: "UPDATE_EVENT_LISTENER_EXPANDED",
|
||||
+expanded: EventListenerExpandedList,
|
||||
|};
|
||||
|
||||
/**
|
||||
* Actions: Source, Breakpoint, and Navigation
|
||||
*
|
||||
@ -180,4 +204,5 @@ export type Action =
|
||||
| FileTextSearchAction
|
||||
| ProjectTextSearchAction
|
||||
| DebuggeeAction
|
||||
| SourceTreeAction;
|
||||
| SourceTreeAction
|
||||
| EventListenerAction;
|
||||
|
@ -47,6 +47,9 @@ export async function onConnect(connection: any, actions: Object) {
|
||||
skipBreakpoints: prefs.skipPausing,
|
||||
});
|
||||
|
||||
// Retrieve possible event listener breakpoints
|
||||
actions.getEventListenerBreakpointTypes();
|
||||
|
||||
// In Firefox, we need to initially request all of the sources. This
|
||||
// usually fires off individual `newSource` notifications as the
|
||||
// debugger finds them, but there may be existing sources already in
|
||||
|
@ -16,7 +16,6 @@ import type {
|
||||
BreakpointLocation,
|
||||
BreakpointOptions,
|
||||
PendingLocation,
|
||||
EventListenerBreakpoints,
|
||||
Frame,
|
||||
FrameId,
|
||||
GeneratedSourceData,
|
||||
@ -36,6 +35,8 @@ import type {
|
||||
SourcesPacket,
|
||||
} from "./types";
|
||||
|
||||
import type { EventListenerCategoryList } from "../../actions/types";
|
||||
|
||||
let workerClients: Object;
|
||||
let threadClient: ThreadClient;
|
||||
let tabTarget: TabTarget;
|
||||
@ -361,8 +362,17 @@ function interrupt(thread: string): Promise<*> {
|
||||
return lookupThreadClient(thread).interrupt();
|
||||
}
|
||||
|
||||
function setEventListenerBreakpoints(eventTypes: EventListenerBreakpoints) {
|
||||
// TODO: Figure out what sendpoint we want to hit
|
||||
async function setEventListenerBreakpoints(ids: string[]) {
|
||||
await threadClient.setActiveEventBreakpoints(ids);
|
||||
await forEachWorkerThread(thread => thread.setActiveEventBreakpoints(ids));
|
||||
}
|
||||
|
||||
// eslint-disable-next-line
|
||||
async function getEventListenerBreakpointTypes(): Promise<
|
||||
EventListenerCategoryList
|
||||
> {
|
||||
const { value } = await threadClient.getAvailableEventBreakpoints();
|
||||
return value;
|
||||
}
|
||||
|
||||
function pauseGrip(thread: string, func: Function): ObjectClient {
|
||||
@ -525,6 +535,7 @@ const clientCommands = {
|
||||
sendPacket,
|
||||
setSkipPausing,
|
||||
setEventListenerBreakpoints,
|
||||
getEventListenerBreakpointTypes,
|
||||
waitForWorkers,
|
||||
detachWorkers,
|
||||
hasWasmSupport,
|
||||
|
@ -25,6 +25,8 @@ import type {
|
||||
Range,
|
||||
} from "../../types";
|
||||
|
||||
import type { EventListenerCategoryList } from "../../actions/types";
|
||||
|
||||
type URL = string;
|
||||
|
||||
/**
|
||||
@ -369,7 +371,10 @@ export type ThreadClient = {
|
||||
actor: ActorId,
|
||||
request: (payload: Object) => Promise<*>,
|
||||
url: string,
|
||||
setEventListenerBreakpoints: (string[]) => void,
|
||||
setActiveEventBreakpoints: (string[]) => void,
|
||||
getAvailableEventBreakpoints: () => Promise<{|
|
||||
value: EventListenerCategoryList,
|
||||
|}>,
|
||||
skipBreakpoints: boolean => Promise<{| skip: boolean |}>,
|
||||
};
|
||||
|
||||
|
@ -15,6 +15,7 @@ import {
|
||||
bootstrapStore,
|
||||
bootstrapWorkers,
|
||||
} from "../utils/bootstrap";
|
||||
|
||||
import { initialBreakpointsState } from "../reducers/breakpoints";
|
||||
|
||||
import type { Panel } from "./firefox/types";
|
||||
@ -47,7 +48,12 @@ async function loadInitialState() {
|
||||
|
||||
const breakpoints = initialBreakpointsState(xhrBreakpoints);
|
||||
|
||||
return { pendingBreakpoints, tabs, breakpoints, eventListenerBreakpoints };
|
||||
return {
|
||||
pendingBreakpoints,
|
||||
tabs,
|
||||
breakpoints,
|
||||
eventListenerBreakpoints,
|
||||
};
|
||||
}
|
||||
|
||||
function getClient(connection: any) {
|
||||
|
@ -9,141 +9,121 @@ import classnames from "classnames";
|
||||
|
||||
import { connect } from "../../utils/connect";
|
||||
import actions from "../../actions";
|
||||
import { getActiveEventListeners } from "../../selectors";
|
||||
import {
|
||||
getActiveEventListeners,
|
||||
getEventListenerBreakpointTypes,
|
||||
getEventListenerExpanded,
|
||||
} from "../../selectors";
|
||||
|
||||
import AccessibleImage from "../shared/AccessibleImage";
|
||||
|
||||
import type { EventListenerBreakpoints } from "../../types";
|
||||
import type {
|
||||
EventListenerActiveList,
|
||||
EventListenerCategoryList,
|
||||
EventListenerExpandedList,
|
||||
} from "../../actions/types";
|
||||
|
||||
import "./EventListeners.css";
|
||||
|
||||
const CATEGORIES = {
|
||||
Mouse: ["click", "mouseover", "dblclick"],
|
||||
Keyboard: ["keyup", "keydown"],
|
||||
};
|
||||
|
||||
type Props = {
|
||||
addEventListeners: typeof actions.addEventListeners,
|
||||
removeEventListeners: typeof actions.removeEventListeners,
|
||||
activeEventListeners: EventListenerBreakpoints,
|
||||
categories: EventListenerCategoryList,
|
||||
expandedCategories: EventListenerExpandedList,
|
||||
activeEventListeners: EventListenerActiveList,
|
||||
addEventListeners: typeof actions.addEventListenerBreakpoints,
|
||||
removeEventListeners: typeof actions.removeEventListenerBreakpoints,
|
||||
addEventListenerExpanded: typeof actions.addEventListenerExpanded,
|
||||
removeEventListenerExpanded: typeof actions.removeEventListenerExpanded,
|
||||
};
|
||||
|
||||
type State = {
|
||||
expandedCategories: string[],
|
||||
};
|
||||
|
||||
function getKey(category: string, eventType: string) {
|
||||
return `${category}:${eventType}`;
|
||||
}
|
||||
|
||||
class EventListeners extends Component<Props, State> {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
expandedCategories: [],
|
||||
};
|
||||
}
|
||||
|
||||
onCategoryToggle(category, event) {
|
||||
const { expandedCategories } = this.state;
|
||||
class EventListeners extends Component<Props> {
|
||||
onCategoryToggle(category) {
|
||||
const {
|
||||
expandedCategories,
|
||||
removeEventListenerExpanded,
|
||||
addEventListenerExpanded,
|
||||
} = this.props;
|
||||
|
||||
if (expandedCategories.includes(category)) {
|
||||
this.setState({
|
||||
expandedCategories: expandedCategories.filter(
|
||||
eventCategory => eventCategory !== category
|
||||
),
|
||||
});
|
||||
removeEventListenerExpanded(category);
|
||||
} else {
|
||||
this.setState({
|
||||
expandedCategories: [...expandedCategories, category],
|
||||
});
|
||||
addEventListenerExpanded(category);
|
||||
}
|
||||
}
|
||||
|
||||
onCategoryClick(category, isChecked) {
|
||||
const { addEventListeners, removeEventListeners } = this.props;
|
||||
const events = CATEGORIES[category].map(eventType =>
|
||||
getKey(category, eventType)
|
||||
);
|
||||
const eventsIds = category.events.map(event => event.id);
|
||||
|
||||
if (isChecked) {
|
||||
addEventListeners(events);
|
||||
addEventListeners(eventsIds);
|
||||
} else {
|
||||
removeEventListeners(events);
|
||||
removeEventListeners(eventsIds);
|
||||
}
|
||||
}
|
||||
|
||||
onEventTypeClick(eventType, isChecked) {
|
||||
onEventTypeClick(eventId, isChecked) {
|
||||
const { addEventListeners, removeEventListeners } = this.props;
|
||||
if (isChecked) {
|
||||
addEventListeners([eventType]);
|
||||
addEventListeners([eventId]);
|
||||
} else {
|
||||
removeEventListeners([eventType]);
|
||||
removeEventListeners([eventId]);
|
||||
}
|
||||
}
|
||||
|
||||
renderCategoryHeading(category) {
|
||||
const { expandedCategories } = this.state;
|
||||
const { activeEventListeners } = this.props;
|
||||
const { activeEventListeners, expandedCategories } = this.props;
|
||||
const { events } = category;
|
||||
|
||||
const eventTypes = CATEGORIES[category];
|
||||
|
||||
const expanded = expandedCategories.includes(category);
|
||||
const checked = eventTypes.every(eventType =>
|
||||
activeEventListeners.includes(getKey(category, eventType))
|
||||
);
|
||||
const expanded = expandedCategories.includes(category.name);
|
||||
const checked = events.every(({ id }) => activeEventListeners.includes(id));
|
||||
const indeterminate =
|
||||
!checked &&
|
||||
eventTypes.some(eventType =>
|
||||
activeEventListeners.includes(getKey(category, eventType))
|
||||
);
|
||||
!checked && events.some(({ id }) => activeEventListeners.includes(id));
|
||||
|
||||
return (
|
||||
<div className="event-listener-header">
|
||||
<button
|
||||
className="event-listener-expand"
|
||||
onClick={e => this.onCategoryToggle(category, e)}
|
||||
onClick={() => this.onCategoryToggle(category.name)}
|
||||
>
|
||||
<AccessibleImage className={classnames("arrow", { expanded })} />
|
||||
</button>
|
||||
<label className="event-listener-label">
|
||||
<input
|
||||
type="checkbox"
|
||||
value={category}
|
||||
value={category.name}
|
||||
onChange={e => this.onCategoryClick(category, e.target.checked)}
|
||||
checked={checked}
|
||||
ref={el => el && (el.indeterminate = indeterminate)}
|
||||
/>
|
||||
<span className="event-listener-category">{category}</span>
|
||||
<span className="event-listener-category">{category.name}</span>
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderCategoryListing(category) {
|
||||
const { activeEventListeners } = this.props;
|
||||
const { expandedCategories } = this.state;
|
||||
const { activeEventListeners, expandedCategories } = this.props;
|
||||
|
||||
const expanded = expandedCategories.includes(category);
|
||||
const expanded = expandedCategories.includes(category.name);
|
||||
if (!expanded) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<ul>
|
||||
{CATEGORIES[category].map(eventType => {
|
||||
const key = getKey(category, eventType);
|
||||
{category.events.map(event => {
|
||||
return (
|
||||
<li className="event-listener-event" key={key}>
|
||||
<li className="event-listener-event" key={event.id}>
|
||||
<label className="event-listener-label">
|
||||
<input
|
||||
type="checkbox"
|
||||
value={key}
|
||||
onChange={e => this.onEventTypeClick(key, e.target.checked)}
|
||||
checked={activeEventListeners.includes(key)}
|
||||
value={event.id}
|
||||
onChange={e =>
|
||||
this.onEventTypeClick(event.id, e.target.checked)
|
||||
}
|
||||
checked={activeEventListeners.includes(event.id)}
|
||||
/>
|
||||
<span className="event-listener-name">{eventType}</span>
|
||||
<span className="event-listener-name">{event.name}</span>
|
||||
</label>
|
||||
</li>
|
||||
);
|
||||
@ -153,12 +133,14 @@ class EventListeners extends Component<Props, State> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { categories } = this.props;
|
||||
|
||||
return (
|
||||
<div className="event-listeners-content">
|
||||
<ul className="event-listeners-list">
|
||||
{Object.keys(CATEGORIES).map(category => {
|
||||
{categories.map((category, index) => {
|
||||
return (
|
||||
<li className="event-listener-group" key={category}>
|
||||
<li className="event-listener-group" key={index}>
|
||||
{this.renderCategoryHeading(category)}
|
||||
{this.renderCategoryListing(category)}
|
||||
</li>
|
||||
@ -170,14 +152,20 @@ class EventListeners extends Component<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
activeEventListeners: getActiveEventListeners(state),
|
||||
});
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
activeEventListeners: getActiveEventListeners(state),
|
||||
categories: getEventListenerBreakpointTypes(state),
|
||||
expandedCategories: getEventListenerExpanded(state),
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
{
|
||||
addEventListeners: actions.addEventListeners,
|
||||
removeEventListeners: actions.removeEventListeners,
|
||||
addEventListeners: actions.addEventListenerBreakpoints,
|
||||
removeEventListeners: actions.removeEventListenerBreakpoints,
|
||||
addEventListenerExpanded: actions.addEventListenerExpanded,
|
||||
removeEventListenerExpanded: actions.removeEventListenerExpanded,
|
||||
}
|
||||
)(EventListeners);
|
||||
|
@ -0,0 +1,82 @@
|
||||
/* 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/>. */
|
||||
|
||||
// @flow
|
||||
|
||||
import React from "react";
|
||||
import { shallow } from "enzyme";
|
||||
import EventListeners from "../EventListeners";
|
||||
|
||||
function getCategories() {
|
||||
return [
|
||||
{
|
||||
name: "Category 1",
|
||||
events: [
|
||||
{ name: "Subcategory 1", id: "category1.subcategory1" },
|
||||
{ name: "Subcategory 2", id: "category1.subcategory2" },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Category 2",
|
||||
events: [
|
||||
{ name: "Subcategory 3", id: "category2.subcategory1" },
|
||||
{ name: "Subcategory 4", id: "category2.subcategory2" },
|
||||
],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
function generateDefaults(overrides = {}) {
|
||||
const defaults = {
|
||||
activeEventListeners: [],
|
||||
expandedCategories: [],
|
||||
categories: [],
|
||||
};
|
||||
|
||||
return { ...defaults, ...overrides };
|
||||
}
|
||||
|
||||
function render(overrides = {}) {
|
||||
const props = generateDefaults(overrides);
|
||||
// $FlowIgnore
|
||||
const component = shallow(<EventListeners.WrappedComponent {...props} />);
|
||||
return { component, props };
|
||||
}
|
||||
|
||||
describe("EventListeners", () => {
|
||||
it("should render", async () => {
|
||||
const { component } = render();
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should render categories appropriately", async () => {
|
||||
const props = {
|
||||
...generateDefaults(),
|
||||
categories: getCategories(),
|
||||
};
|
||||
const { component } = render(props);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should render expanded categories appropriately", async () => {
|
||||
const props = {
|
||||
...generateDefaults(),
|
||||
categories: getCategories(),
|
||||
expandedCategories: ["Category 2"],
|
||||
};
|
||||
const { component } = render(props);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should render checked subcategories appropriately", async () => {
|
||||
const props = {
|
||||
...generateDefaults(),
|
||||
categories: getCategories(),
|
||||
activeEventListeners: ["category1.subcategory2"],
|
||||
expandedCategories: ["Category 1"],
|
||||
};
|
||||
const { component } = render(props);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
});
|
@ -0,0 +1,320 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`EventListeners should render 1`] = `
|
||||
<div
|
||||
className="event-listeners-content"
|
||||
>
|
||||
<ul
|
||||
className="event-listeners-list"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`EventListeners should render categories appropriately 1`] = `
|
||||
<div
|
||||
className="event-listeners-content"
|
||||
>
|
||||
<ul
|
||||
className="event-listeners-list"
|
||||
>
|
||||
<li
|
||||
className="event-listener-group"
|
||||
key="0"
|
||||
>
|
||||
<div
|
||||
className="event-listener-header"
|
||||
>
|
||||
<button
|
||||
className="event-listener-expand"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<AccessibleImage
|
||||
className="arrow"
|
||||
/>
|
||||
</button>
|
||||
<label
|
||||
className="event-listener-label"
|
||||
>
|
||||
<input
|
||||
checked={false}
|
||||
onChange={[Function]}
|
||||
type="checkbox"
|
||||
value="Category 1"
|
||||
/>
|
||||
<span
|
||||
className="event-listener-category"
|
||||
>
|
||||
Category 1
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
className="event-listener-group"
|
||||
key="1"
|
||||
>
|
||||
<div
|
||||
className="event-listener-header"
|
||||
>
|
||||
<button
|
||||
className="event-listener-expand"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<AccessibleImage
|
||||
className="arrow"
|
||||
/>
|
||||
</button>
|
||||
<label
|
||||
className="event-listener-label"
|
||||
>
|
||||
<input
|
||||
checked={false}
|
||||
onChange={[Function]}
|
||||
type="checkbox"
|
||||
value="Category 2"
|
||||
/>
|
||||
<span
|
||||
className="event-listener-category"
|
||||
>
|
||||
Category 2
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`EventListeners should render checked subcategories appropriately 1`] = `
|
||||
<div
|
||||
className="event-listeners-content"
|
||||
>
|
||||
<ul
|
||||
className="event-listeners-list"
|
||||
>
|
||||
<li
|
||||
className="event-listener-group"
|
||||
key="0"
|
||||
>
|
||||
<div
|
||||
className="event-listener-header"
|
||||
>
|
||||
<button
|
||||
className="event-listener-expand"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<AccessibleImage
|
||||
className="arrow expanded"
|
||||
/>
|
||||
</button>
|
||||
<label
|
||||
className="event-listener-label"
|
||||
>
|
||||
<input
|
||||
checked={false}
|
||||
onChange={[Function]}
|
||||
type="checkbox"
|
||||
value="Category 1"
|
||||
/>
|
||||
<span
|
||||
className="event-listener-category"
|
||||
>
|
||||
Category 1
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<ul>
|
||||
<li
|
||||
className="event-listener-event"
|
||||
key="category1.subcategory1"
|
||||
>
|
||||
<label
|
||||
className="event-listener-label"
|
||||
>
|
||||
<input
|
||||
checked={false}
|
||||
onChange={[Function]}
|
||||
type="checkbox"
|
||||
value="category1.subcategory1"
|
||||
/>
|
||||
<span
|
||||
className="event-listener-name"
|
||||
>
|
||||
Subcategory 1
|
||||
</span>
|
||||
</label>
|
||||
</li>
|
||||
<li
|
||||
className="event-listener-event"
|
||||
key="category1.subcategory2"
|
||||
>
|
||||
<label
|
||||
className="event-listener-label"
|
||||
>
|
||||
<input
|
||||
checked={true}
|
||||
onChange={[Function]}
|
||||
type="checkbox"
|
||||
value="category1.subcategory2"
|
||||
/>
|
||||
<span
|
||||
className="event-listener-name"
|
||||
>
|
||||
Subcategory 2
|
||||
</span>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li
|
||||
className="event-listener-group"
|
||||
key="1"
|
||||
>
|
||||
<div
|
||||
className="event-listener-header"
|
||||
>
|
||||
<button
|
||||
className="event-listener-expand"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<AccessibleImage
|
||||
className="arrow"
|
||||
/>
|
||||
</button>
|
||||
<label
|
||||
className="event-listener-label"
|
||||
>
|
||||
<input
|
||||
checked={false}
|
||||
onChange={[Function]}
|
||||
type="checkbox"
|
||||
value="Category 2"
|
||||
/>
|
||||
<span
|
||||
className="event-listener-category"
|
||||
>
|
||||
Category 2
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`EventListeners should render expanded categories appropriately 1`] = `
|
||||
<div
|
||||
className="event-listeners-content"
|
||||
>
|
||||
<ul
|
||||
className="event-listeners-list"
|
||||
>
|
||||
<li
|
||||
className="event-listener-group"
|
||||
key="0"
|
||||
>
|
||||
<div
|
||||
className="event-listener-header"
|
||||
>
|
||||
<button
|
||||
className="event-listener-expand"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<AccessibleImage
|
||||
className="arrow"
|
||||
/>
|
||||
</button>
|
||||
<label
|
||||
className="event-listener-label"
|
||||
>
|
||||
<input
|
||||
checked={false}
|
||||
onChange={[Function]}
|
||||
type="checkbox"
|
||||
value="Category 1"
|
||||
/>
|
||||
<span
|
||||
className="event-listener-category"
|
||||
>
|
||||
Category 1
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
className="event-listener-group"
|
||||
key="1"
|
||||
>
|
||||
<div
|
||||
className="event-listener-header"
|
||||
>
|
||||
<button
|
||||
className="event-listener-expand"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<AccessibleImage
|
||||
className="arrow expanded"
|
||||
/>
|
||||
</button>
|
||||
<label
|
||||
className="event-listener-label"
|
||||
>
|
||||
<input
|
||||
checked={false}
|
||||
onChange={[Function]}
|
||||
type="checkbox"
|
||||
value="Category 2"
|
||||
/>
|
||||
<span
|
||||
className="event-listener-category"
|
||||
>
|
||||
Category 2
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<ul>
|
||||
<li
|
||||
className="event-listener-event"
|
||||
key="category2.subcategory1"
|
||||
>
|
||||
<label
|
||||
className="event-listener-label"
|
||||
>
|
||||
<input
|
||||
checked={false}
|
||||
onChange={[Function]}
|
||||
type="checkbox"
|
||||
value="category2.subcategory1"
|
||||
/>
|
||||
<span
|
||||
className="event-listener-name"
|
||||
>
|
||||
Subcategory 3
|
||||
</span>
|
||||
</label>
|
||||
</li>
|
||||
<li
|
||||
className="event-listener-event"
|
||||
key="category2.subcategory2"
|
||||
>
|
||||
<label
|
||||
className="event-listener-label"
|
||||
>
|
||||
<input
|
||||
checked={false}
|
||||
onChange={[Function]}
|
||||
type="checkbox"
|
||||
value="category2.subcategory2"
|
||||
/>
|
||||
<span
|
||||
className="event-listener-name"
|
||||
>
|
||||
Subcategory 4
|
||||
</span>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
`;
|
@ -4,45 +4,61 @@
|
||||
|
||||
// @flow
|
||||
|
||||
import { uniq } from "lodash";
|
||||
import type { State } from "./types";
|
||||
import type {
|
||||
EventListenerAction,
|
||||
EventListenerActiveList,
|
||||
EventListenerCategoryList,
|
||||
EventListenerExpandedList,
|
||||
} from "../actions/types";
|
||||
|
||||
import { asyncStore } from "../utils/prefs";
|
||||
import type { EventListenerBreakpoints } from "../types";
|
||||
export type EventListenersState = {
|
||||
active: EventListenerActiveList,
|
||||
categories: EventListenerCategoryList,
|
||||
expanded: EventListenerExpandedList,
|
||||
};
|
||||
|
||||
type OuterState = { eventListenerBreakpoints: EventListenerBreakpoints };
|
||||
export function initialEventListenerState(): EventListenersState {
|
||||
return {
|
||||
active: [],
|
||||
categories: [],
|
||||
expanded: [],
|
||||
};
|
||||
}
|
||||
|
||||
function update(state: EventListenerBreakpoints = [], action: any) {
|
||||
function update(
|
||||
state: EventListenersState = initialEventListenerState(),
|
||||
action: EventListenerAction
|
||||
) {
|
||||
switch (action.type) {
|
||||
case "ADD_EVENT_LISTENERS":
|
||||
return updateEventTypes("add", state, action.events);
|
||||
case "UPDATE_EVENT_LISTENERS":
|
||||
return { ...state, active: action.active };
|
||||
|
||||
case "REMOVE_EVENT_LISTENERS":
|
||||
return updateEventTypes("remove", state, action.events);
|
||||
case "RECEIVE_EVENT_LISTENER_TYPES":
|
||||
return { ...state, categories: action.categories };
|
||||
|
||||
case "UPDATE_EVENT_LISTENER_EXPANDED":
|
||||
return { ...state, expanded: action.expanded };
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
function updateEventTypes(
|
||||
addOrRemove: string,
|
||||
currentEvents: EventListenerBreakpoints,
|
||||
events: EventListenerBreakpoints
|
||||
): EventListenerBreakpoints {
|
||||
let newEventListeners;
|
||||
|
||||
if (addOrRemove === "add") {
|
||||
newEventListeners = uniq([...currentEvents, ...events]);
|
||||
} else {
|
||||
newEventListeners = currentEvents.filter(event => !events.includes(event));
|
||||
}
|
||||
|
||||
asyncStore.eventListenerBreakpoints = newEventListeners;
|
||||
return newEventListeners;
|
||||
export function getActiveEventListeners(state: State): EventListenerActiveList {
|
||||
return state.eventListenerBreakpoints.active;
|
||||
}
|
||||
|
||||
export function getActiveEventListeners(state: OuterState) {
|
||||
return state.eventListenerBreakpoints;
|
||||
export function getEventListenerBreakpointTypes(
|
||||
state: State
|
||||
): EventListenerCategoryList {
|
||||
return state.eventListenerBreakpoints.categories;
|
||||
}
|
||||
|
||||
export function getEventListenerExpanded(
|
||||
state: State
|
||||
): EventListenerExpandedList {
|
||||
return state.eventListenerBreakpoints.expanded;
|
||||
}
|
||||
|
||||
export default update;
|
||||
|
@ -24,11 +24,13 @@ import type { SourceActorsState } from "./source-actors";
|
||||
import type { TabList } from "./tabs";
|
||||
import type { UIState } from "./ui";
|
||||
import type { QuickOpenState } from "./quick-open";
|
||||
import type { EventListenersState } from "./event-listeners";
|
||||
|
||||
export type State = {
|
||||
ast: ASTState,
|
||||
breakpoints: BreakpointsState,
|
||||
expressions: Record<ExpressionState>,
|
||||
eventListenerBreakpoints: EventListenersState,
|
||||
debuggee: DebuggeeState,
|
||||
fileSearch: Record<FileSearchState>,
|
||||
pause: PauseState,
|
||||
|
@ -12,7 +12,7 @@ import { asyncStoreHelper } from "./asyncStoreHelper";
|
||||
// Schema version to bump when the async store format has changed incompatibly
|
||||
// and old stores should be cleared. This needs to match the prefs schema
|
||||
// version in devtools/client/preferences/debugger.js.
|
||||
const prefsSchemaVersion = "1.0.9";
|
||||
const prefsSchemaVersion = "1.0.10";
|
||||
const pref = Services.pref;
|
||||
|
||||
if (isDevelopment()) {
|
||||
@ -134,7 +134,7 @@ export const asyncStore = asyncStoreHelper("debugger", {
|
||||
pendingBreakpoints: ["pending-breakpoints", {}],
|
||||
tabs: ["tabs", []],
|
||||
xhrBreakpoints: ["xhr-breakpoints", []],
|
||||
eventListenerBreakpoints: ["event-listener-breakpoints", []],
|
||||
eventListenerBreakpoints: ["event-listener-breakpoints", undefined],
|
||||
});
|
||||
|
||||
export function verifyPrefSchema() {
|
||||
@ -143,6 +143,7 @@ export function verifyPrefSchema() {
|
||||
asyncStore.pendingBreakpoints = {};
|
||||
asyncStore.tabs = [];
|
||||
asyncStore.xhrBreakpoints = [];
|
||||
asyncStore.eventListenerBreakpoints = undefined;
|
||||
prefs.debuggerPrefsSchemaVersion = prefsSchemaVersion;
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ pref("devtools.debugger.workers", false);
|
||||
|
||||
// The default Debugger UI settings
|
||||
// This schema version needs to match that in devtools/client/debugger/src/utils/prefs.js.
|
||||
pref("devtools.debugger.prefs-schema-version", "1.0.9");
|
||||
pref("devtools.debugger.prefs-schema-version", "1.0.10");
|
||||
pref("devtools.debugger.ui.panes-workers-and-sources-width", 200);
|
||||
pref("devtools.debugger.ui.panes-instruments-width", 300);
|
||||
pref("devtools.debugger.ui.panes-visible-on-startup", false);
|
||||
@ -46,7 +46,6 @@ pref("devtools.debugger.tabsBlackBoxed", "[]");
|
||||
pref("devtools.debugger.pending-selected-location", "{}");
|
||||
pref("devtools.debugger.pending-breakpoints", "{}");
|
||||
pref("devtools.debugger.expressions", "[]");
|
||||
pref("devtools.debugger.event-listener-breakpoints", "[]");
|
||||
pref("devtools.debugger.file-search-case-sensitive", false);
|
||||
pref("devtools.debugger.file-search-whole-word", false);
|
||||
pref("devtools.debugger.file-search-regex-match", false);
|
||||
|
Loading…
Reference in New Issue
Block a user