diff --git a/client/components/Button.jsx b/client/components/Button.jsx
index 9f2ed889..99bff88c 100644
--- a/client/components/Button.jsx
+++ b/client/components/Button.jsx
@@ -1,9 +1,22 @@
import cls from "classnames";
+import PropTypes from "prop-types";
import PureComponent from "../lib/PureComponent.jsx";
import * as styles from "./Button.css";
export default class Button extends PureComponent {
+ static propTypes = {
+ className: PropTypes.string,
+
+ active: PropTypes.bool,
+ toggle: PropTypes.bool,
+ disabled: PropTypes.bool,
+
+ onClick: PropTypes.func.isRequired,
+
+ children: PropTypes.node,
+ };
+
render({ active, className, children, ...props }) {
const classes = cls(className, {
[styles.button]: true,
@@ -25,12 +38,15 @@ export default class Button extends PureComponent {
}
get disabled() {
- const { props } = this;
- return props.disabled || (props.active && !props.toggle);
+ const { disabled, active, toggle } = this.props;
+ return disabled || (active && !toggle);
}
handleClick = (event) => {
- this.elem.blur();
+ if (this.elem) {
+ this.elem.blur();
+ }
+
this.props.onClick(event);
};
diff --git a/client/components/Checkbox.jsx b/client/components/Checkbox.jsx
index be2c1ed3..2e64366c 100644
--- a/client/components/Checkbox.jsx
+++ b/client/components/Checkbox.jsx
@@ -1,9 +1,20 @@
import cls from "classnames";
import { Component } from "preact";
+import PropTypes from "prop-types";
import * as styles from "./Checkbox.css";
export default class Checkbox extends Component {
+ static propTypes = {
+ className: PropTypes.string,
+
+ checked: PropTypes.bool,
+
+ onChange: PropTypes.func.isRequired,
+
+ children: PropTypes.node,
+ };
+
render() {
const { checked, className, children } = this.props;
@@ -15,7 +26,7 @@ export default class Checkbox extends Component {
checked={checked}
onChange={this.handleChange}
/>
- {children}
+ {children && {children}}
);
}
diff --git a/client/components/CheckboxList.jsx b/client/components/CheckboxList.jsx
index 984c0b5e..e737bb40 100644
--- a/client/components/CheckboxList.jsx
+++ b/client/components/CheckboxList.jsx
@@ -1,10 +1,21 @@
+import PropTypes from "prop-types";
import PureComponent from "../lib/PureComponent.jsx";
import * as styles from "./CheckboxList.css";
import CheckboxListItem from "./CheckboxListItem.jsx";
+import { ViewerDataType } from "./types.js";
const ALL_ITEM = Symbol("ALL_ITEM");
export default class CheckboxList extends PureComponent {
+ static propTypes = {
+ label: PropTypes.string.isRequired,
+ renderLabel: PropTypes.func.isRequired,
+ items: PropTypes.oneOfType([ViewerDataType, PropTypes.symbol]),
+ checkedItems: PropTypes.oneOfType([ViewerDataType, PropTypes.symbol]),
+
+ onChange: PropTypes.func.isRequired,
+ };
+
static ALL_ITEM = ALL_ITEM;
constructor(props) {
diff --git a/client/components/CheckboxListItem.jsx b/client/components/CheckboxListItem.jsx
index eb36b9c9..626e7150 100644
--- a/client/components/CheckboxListItem.jsx
+++ b/client/components/CheckboxListItem.jsx
@@ -1,10 +1,21 @@
import { Component } from "preact";
+import PropTypes from "prop-types";
import Checkbox from "./Checkbox.jsx";
import * as styles from "./CheckboxList.css";
import CheckboxList from "./CheckboxList.jsx";
+import { ViewerDataItemType } from "./types.js";
export default class CheckboxListItem extends Component {
+ static propTypes = {
+ item: PropTypes.oneOfType([ViewerDataItemType, PropTypes.symbol])
+ .isRequired,
+
+ onChange: PropTypes.func.isRequired,
+
+ children: PropTypes.func,
+ };
+
render() {
return (
@@ -17,7 +28,6 @@ export default class CheckboxListItem extends Component {
renderLabel() {
const { children, item } = this.props;
-
if (children) {
return children(item);
}
diff --git a/client/components/ContextMenu.jsx b/client/components/ContextMenu.jsx
index 8228dfb0..1db649fa 100644
--- a/client/components/ContextMenu.jsx
+++ b/client/components/ContextMenu.jsx
@@ -1,11 +1,25 @@
import cls from "classnames";
+import PropTypes from "prop-types";
import PureComponent from "../lib/PureComponent.jsx";
import { store } from "../store.js";
import { elementIsOutside } from "../utils.js";
import * as styles from "./ContextMenu.css";
import ContextMenuItem from "./ContextMenuItem.jsx";
+import { ViewerDataItemType } from "./types.js";
export default class ContextMenu extends PureComponent {
+ static propTypes = {
+ visible: PropTypes.bool,
+
+ chunk: ViewerDataItemType,
+ coords: PropTypes.shape({
+ x: PropTypes.number,
+ y: PropTypes.number,
+ }).isRequired,
+
+ onHide: PropTypes.func,
+ };
+
componentDidMount() {
this.boundingRect = this.node.getBoundingClientRect();
}
@@ -68,24 +82,25 @@ export default class ContextMenu extends PureComponent {
const filteredChunks = store.selectedChunks.filter(
(chunk) => chunk.label !== selectedChunk.label,
);
- store.selectedChunks = filteredChunks;
+ store.setSelectedChunks(filteredChunks);
}
this.hide();
};
handleClickFilterToChunk = () => {
const { chunk: selectedChunk } = this.props;
+
if (selectedChunk && selectedChunk.label) {
const filteredChunks = store.allChunks.filter(
(chunk) => chunk.label === selectedChunk.label,
);
- store.selectedChunks = filteredChunks;
+ store.setSelectedChunks(filteredChunks);
}
this.hide();
};
handleClickShowAllChunks = () => {
- store.selectedChunks = store.allChunks;
+ store.setSelectedChunks(store.allChunks);
this.hide();
};
diff --git a/client/components/ContextMenuItem.jsx b/client/components/ContextMenuItem.jsx
index d44f398b..3508e9d3 100644
--- a/client/components/ContextMenuItem.jsx
+++ b/client/components/ContextMenuItem.jsx
@@ -1,4 +1,5 @@
import cls from "classnames";
+import PropTypes from "prop-types";
import * as styles from "./ContextMenuItem.css";
@@ -18,3 +19,11 @@ export default function ContextMenuItem({ children, disabled, onClick }) {
);
}
+
+ContextMenuItem.propTypes = {
+ disabled: PropTypes.bool,
+
+ children: PropTypes.node.isRequired,
+
+ onClick: PropTypes.func,
+};
diff --git a/client/components/Dropdown.jsx b/client/components/Dropdown.jsx
index db306e11..7a415e41 100644
--- a/client/components/Dropdown.jsx
+++ b/client/components/Dropdown.jsx
@@ -1,9 +1,16 @@
import { createRef } from "preact";
+import PropTypes from "prop-types";
import PureComponent from "../lib/PureComponent.jsx";
import * as styles from "./Dropdown.css";
export default class Dropdown extends PureComponent {
+ static propTypes = {
+ label: PropTypes.string.isRequired,
+ options: PropTypes.arrayOf(PropTypes.string).isRequired,
+ onSelectionChange: PropTypes.func.isRequired,
+ };
+
input = createRef();
state = {
diff --git a/client/components/Icon.jsx b/client/components/Icon.jsx
index d91930af..fbc59b1d 100644
--- a/client/components/Icon.jsx
+++ b/client/components/Icon.jsx
@@ -1,4 +1,5 @@
import cls from "classnames";
+import PropTypes from "prop-types";
import iconArrowRight from "../assets/icon-arrow-right.svg";
import iconMoon from "../assets/icon-moon.svg";
import iconPin from "../assets/icon-pin.svg";
@@ -27,6 +28,14 @@ const ICONS = {
};
export default class Icon extends PureComponent {
+ static propTypes = {
+ className: PropTypes.string,
+
+ name: PropTypes.string.isRequired,
+ size: PropTypes.number,
+ rotate: PropTypes.number,
+ };
+
render({ className }) {
return
;
}
diff --git a/client/components/ModuleItem.jsx b/client/components/ModuleItem.jsx
index 6871622f..189a362d 100644
--- a/client/components/ModuleItem.jsx
+++ b/client/components/ModuleItem.jsx
@@ -2,11 +2,22 @@ import cls from "classnames";
import escapeRegExp from "escape-string-regexp";
import { filesize } from "filesize";
import { escape } from "html-escaper";
+import PropTypes from "prop-types";
import PureComponent from "../lib/PureComponent.jsx";
-
import * as styles from "./ModuleItem.css";
+import { ModuleType, SizeType } from "./types.js";
export default class ModuleItem extends PureComponent {
+ static propTypes = {
+ module: ModuleType.isRequired,
+ showSize: SizeType.isRequired,
+ highlightedText: PropTypes.instanceOf(RegExp),
+
+ isVisible: PropTypes.func.isRequired,
+
+ onClick: PropTypes.func.isRequired,
+ };
+
state = {
visible: true,
};
diff --git a/client/components/ModulesList.jsx b/client/components/ModulesList.jsx
index 50b605a6..1404d2ec 100644
--- a/client/components/ModulesList.jsx
+++ b/client/components/ModulesList.jsx
@@ -1,10 +1,22 @@
import cls from "classnames";
+import PropTypes from "prop-types";
import PureComponent from "../lib/PureComponent.jsx";
import ModuleItem from "./ModuleItem.jsx";
-
import * as styles from "./ModulesList.css";
+import { ModuleType, SizeType } from "./types.js";
export default class ModulesList extends PureComponent {
+ static propTypes = {
+ className: PropTypes.string,
+
+ modules: PropTypes.arrayOf(ModuleType).isRequired,
+ showSize: SizeType.isRequired,
+ highlightedText: PropTypes.instanceOf(RegExp),
+
+ isModuleVisible: PropTypes.func.isRequired,
+ onModuleClick: PropTypes.func.isRequired,
+ };
+
render({ modules, showSize, highlightedText, isModuleVisible, className }) {
return (
diff --git a/client/components/ModulesTreemap.jsx b/client/components/ModulesTreemap.jsx
index 81de5f38..7a2a9833 100644
--- a/client/components/ModulesTreemap.jsx
+++ b/client/components/ModulesTreemap.jsx
@@ -173,7 +173,9 @@ class ModulesTreemap extends Component {
onGroupSecondaryClick={this.handleTreemapGroupSecondaryClick}
onResize={this.handleResize}
/>
- {tooltipContent}
+ {tooltipContent && (
+ {tooltipContent}
+ )}
{
if (!selected) {
- store.selectedChunks = store.allChunks;
+ store.setSelectedChunks(store.allChunks);
return;
}
- store.selectedChunks = store.allChunks.filter(
- (chunk) => chunk.isInitialByEntrypoint[selected] ?? false,
+ store.setSelectedChunks(
+ store.allChunks.filter(
+ (chunk) => chunk.isInitialByEntrypoint[selected] ?? false,
+ ),
);
};
@@ -314,15 +318,15 @@ class ModulesTreemap extends Component {
};
handleSizeSwitch = (sizeSwitchItem) => {
- store.selectedSize = sizeSwitchItem.prop;
+ store.setSelectedSize(sizeSwitchItem.prop);
};
handleQueryChange = (query) => {
- store.searchQuery = query;
+ store.setSearchQuery(query);
};
handleSelectedChunksChange = (selectedChunks) => {
- store.selectedChunks = selectedChunks;
+ store.setSelectedSize(selectedChunks);
};
handleMouseLeaveTreemap = () => {
diff --git a/client/components/Search.jsx b/client/components/Search.jsx
index ff207b7b..4aaf9263 100644
--- a/client/components/Search.jsx
+++ b/client/components/Search.jsx
@@ -1,12 +1,24 @@
// TODO: switch to a more modern debounce package once we drop Node.js 10 support
import debounce from "debounce";
+import PropTypes from "prop-types";
import PureComponent from "../lib/PureComponent.jsx";
import Button from "./Button.jsx";
import * as styles from "./Search.css";
export default class Search extends PureComponent {
+ static propTypes = {
+ className: PropTypes.string,
+
+ label: PropTypes.string.isRequired,
+ query: PropTypes.string.isRequired,
+
+ autofocus: PropTypes.bool,
+
+ onQueryChange: PropTypes.func.isRequired,
+ };
+
componentDidMount() {
if (this.props.autofocus) {
this.focus();
diff --git a/client/components/Sidebar.jsx b/client/components/Sidebar.jsx
index f859aaa9..db5351c6 100644
--- a/client/components/Sidebar.jsx
+++ b/client/components/Sidebar.jsx
@@ -1,6 +1,6 @@
import cls from "classnames";
import { Component } from "preact";
-
+import PropTypes from "prop-types";
import Button from "./Button.jsx";
import Icon from "./Icon.jsx";
import * as styles from "./Sidebar.css";
@@ -9,6 +9,17 @@ import ThemeToggle from "./ThemeToggle.jsx";
const toggleTime = Number.parseInt(styles.toggleTime, 10);
export default class Sidebar extends Component {
+ static propTypes = {
+ pinned: PropTypes.bool.isRequired,
+ position: PropTypes.string,
+
+ onToggle: PropTypes.func.isRequired,
+ onResize: PropTypes.func.isRequired,
+ onPinStateChange: PropTypes.func.isRequired,
+
+ children: PropTypes.node.isRequired,
+ };
+
static defaultProps = {
pinned: false,
position: "left",
diff --git a/client/components/Switcher.jsx b/client/components/Switcher.jsx
index f3ed7834..6b9c0227 100644
--- a/client/components/Switcher.jsx
+++ b/client/components/Switcher.jsx
@@ -1,8 +1,19 @@
+import PropTypes from "prop-types";
import PureComponent from "../lib/PureComponent.jsx";
import * as styles from "./Switcher.css";
import SwitcherItem from "./SwitcherItem.jsx";
+import { SwitcherItemType } from "./types.js";
export default class Switcher extends PureComponent {
+ static propTypes = {
+ label: PropTypes.string.isRequired,
+
+ items: PropTypes.arrayOf(SwitcherItemType).isRequired,
+ activeItem: SwitcherItemType.isRequired,
+
+ onSwitch: PropTypes.func.isRequired,
+ };
+
render() {
const { label, items, activeItem, onSwitch } = this.props;
diff --git a/client/components/SwitcherItem.jsx b/client/components/SwitcherItem.jsx
index 8f32c704..80b70941 100644
--- a/client/components/SwitcherItem.jsx
+++ b/client/components/SwitcherItem.jsx
@@ -1,7 +1,17 @@
+import PropTypes from "prop-types";
import PureComponent from "../lib/PureComponent.jsx";
import Button from "./Button.jsx";
+import { SwitcherItemType } from "./types.js";
export default class SwitcherItem extends PureComponent {
+ static propTypes = {
+ active: PropTypes.bool.isRequired,
+
+ item: SwitcherItemType.isRequired,
+
+ onClick: PropTypes.func.isRequired,
+ };
+
render({ item, ...props }) {
return (