Convert Explorer into TypeScript

And replace PropTypes with TypeScript annotations
pull/6478/head
Karl Hobley 2020-10-19 18:30:43 +01:00 zatwierdzone przez LB (Ben Johnston)
rodzic 60af9ccef3
commit 650045b573
12 zmienionych plików z 131 dodań i 76 usunięć

Wyświetl plik

@ -1,4 +1,5 @@
import PropTypes from 'prop-types';
/* eslint-disable react/prop-types */
import React from 'react';
import { connect } from 'react-redux';
@ -6,7 +7,16 @@ import * as actions from './actions';
import ExplorerPanel from './ExplorerPanel';
const Explorer = ({
interface ExplorerProps {
isVisible: boolean;
path: number[],
nodes: any,
onClose(): void;
popPage(): void;
pushPage(id: number): void;
}
const Explorer: React.FunctionComponent<ExplorerProps> = ({
isVisible,
nodes,
path,
@ -28,16 +38,6 @@ const Explorer = ({
) : null;
};
Explorer.propTypes = {
isVisible: PropTypes.bool.isRequired,
path: PropTypes.array.isRequired,
nodes: PropTypes.object.isRequired,
pushPage: PropTypes.func.isRequired,
popPage: PropTypes.func.isRequired,
onClose: PropTypes.func.isRequired,
};
const mapStateToProps = (state) => ({
isVisible: state.explorer.isVisible,
path: state.explorer.path,

Wyświetl plik

@ -1,15 +1,26 @@
import PropTypes from 'prop-types';
/* eslint-disable react/prop-types */
import React from 'react';
import { ADMIN_URLS, STRINGS } from '../../config/wagtailConfig';
import Button from '../../components/Button/Button';
import Icon from '../../components/Icon/Icon';
interface ExplorerHeaderProps {
page: {
id: string | number;
/* eslint-disable-next-line camelcase */
admin_display_title: string;
};
depth: number;
onClick(eL: any): void
}
/**
* The bar at the top of the explorer, displaying the current level
* and allowing access back to the parent level.
*/
const ExplorerHeader = ({ page, depth, onClick }) => {
const ExplorerHeader: React.FunctionComponent<ExplorerHeaderProps> = ({ page, depth, onClick }) => {
const isRoot = depth === 1;
return (
@ -29,13 +40,4 @@ const ExplorerHeader = ({ page, depth, onClick }) => {
);
};
ExplorerHeader.propTypes = {
page: PropTypes.shape({
id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
admin_display_title: PropTypes.string,
}).isRequired,
depth: PropTypes.number.isRequired,
onClick: PropTypes.func.isRequired,
};
export default ExplorerHeader;

Wyświetl plik

@ -1,4 +1,5 @@
import PropTypes from 'prop-types';
/* eslint-disable react/prop-types */
import React from 'react';
import { ADMIN_URLS, STRINGS } from '../../config/wagtailConfig';
@ -11,11 +12,26 @@ const childrenIcon = (
<Icon name="folder-inverse" className="icon--menuitem" />
);
interface ExplorerItemProps {
item: {
id: number;
/* eslint-disable-next-line camelcase */
admin_display_title: string;
meta: {
status: any;
children: {
count: number;
}
};
};
onClick(): void;
}
/**
* One menu item in the page explorer, with different available actions
* and information depending on the metadata of the page.
*/
const ExplorerItem = ({ item, onClick }) => {
const ExplorerItem: React.FunctionComponent<ExplorerItemProps> = ({ item, onClick }) => {
const { id, admin_display_title: title, meta } = item;
const hasChildren = meta.children.count > 0;
const isPublished = meta.status.live && !meta.status.has_unpublished_changes;
@ -58,15 +74,4 @@ const ExplorerItem = ({ item, onClick }) => {
);
};
ExplorerItem.propTypes = {
item: PropTypes.shape({
id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
admin_display_title: PropTypes.string.isRequired,
meta: PropTypes.shape({
status: PropTypes.object.isRequired,
}).isRequired,
}).isRequired,
onClick: PropTypes.func.isRequired,
};
export default ExplorerItem;

Wyświetl plik

@ -1,4 +1,5 @@
import PropTypes from 'prop-types';
/* eslint-disable react/prop-types */
import React from 'react';
import FocusTrap from 'focus-trap-react';
@ -11,11 +12,35 @@ import ExplorerHeader from './ExplorerHeader';
import ExplorerItem from './ExplorerItem';
import PageCount from './PageCount';
interface ExplorerPanelProps {
nodes: any;
path: number[];
page: {
id: number;
/* eslint-disable-next-line camelcase */
admin_display_title: string;
isFetching: boolean;
isError: boolean;
children: {
count: number;
items: any[];
};
};
onClose(): void;
popPage(): void;
pushPage(id: number): void;
}
interface ExplorerPanelState {
transition: typeof PUSH | typeof POP;
paused: boolean;
}
/**
* The main panel of the page explorer menu, with heading,
* menu items, and special states.
*/
class ExplorerPanel extends React.Component {
class ExplorerPanel extends React.Component<ExplorerPanelProps, ExplorerPanelState> {
constructor(props) {
super(props);
@ -39,14 +64,14 @@ class ExplorerPanel extends React.Component {
}
componentDidMount() {
document.querySelector('[data-explorer-menu-item]').classList.add('submenu-active');
document.querySelector('[data-explorer-menu-item]')?.classList.add('submenu-active');
document.body.classList.add('explorer-open');
document.addEventListener('mousedown', this.clickOutside);
document.addEventListener('touchend', this.clickOutside);
}
componentWillUnmount() {
document.querySelector('[data-explorer-menu-item]').classList.remove('submenu-active');
document.querySelector('[data-explorer-menu-item]')?.classList.remove('submenu-active');
document.body.classList.remove('explorer-open');
document.removeEventListener('mousedown', this.clickOutside);
document.removeEventListener('touchend', this.clickOutside);
@ -57,6 +82,10 @@ class ExplorerPanel extends React.Component {
const explorer = document.querySelector('[data-explorer-menu]');
const toggle = document.querySelector('[data-explorer-menu-item]');
if (!explorer || !toggle) {
return;
}
const isInside = explorer.contains(e.target) || toggle.contains(e.target);
if (!isInside) {
onClose();
@ -169,19 +198,4 @@ class ExplorerPanel extends React.Component {
}
}
ExplorerPanel.propTypes = {
nodes: PropTypes.object.isRequired,
path: PropTypes.array.isRequired,
page: PropTypes.shape({
isFetching: PropTypes.bool,
children: PropTypes.shape({
count: PropTypes.number,
items: PropTypes.array,
}),
}).isRequired,
onClose: PropTypes.func.isRequired,
popPage: PropTypes.func.isRequired,
pushPage: PropTypes.func.isRequired,
};
export default ExplorerPanel;

Wyświetl plik

@ -1,4 +1,5 @@
import PropTypes from 'prop-types';
/* eslint-disable react/prop-types */
import React from 'react';
import { connect } from 'react-redux';
@ -7,10 +8,15 @@ import * as actions from './actions';
import Button from '../../components/Button/Button';
import Icon from '../../components/Icon/Icon';
interface ExplorerToggleProps {
onToggle(): void;
children: React.ReactNode;
}
/**
* A Button which toggles the explorer.
*/
const ExplorerToggle = ({ children, onToggle }) => (
const ExplorerToggle: React.FunctionComponent<ExplorerToggleProps> = ({ children, onToggle }) => (
<Button
dialogTrigger={true}
onClick={onToggle}
@ -21,18 +27,13 @@ const ExplorerToggle = ({ children, onToggle }) => (
</Button>
);
ExplorerToggle.propTypes = {
onToggle: PropTypes.func.isRequired,
children: PropTypes.node.isRequired,
};
const mapStateToProps = () => ({});
const mapDispatchToProps = (dispatch) => ({
onToggle: (page) => dispatch(actions.toggleExplorer(page)),
});
const mergeProps = (stateProps, dispatchProps, ownProps) => ({
const mergeProps = (_stateProps, dispatchProps, ownProps) => ({
children: ownProps.children,
onToggle: dispatchProps.onToggle.bind(null, ownProps.startPage),
});

Wyświetl plik

@ -1,10 +1,20 @@
import PropTypes from 'prop-types';
/* eslint-disable react/prop-types */
import React from 'react';
import { ADMIN_URLS, STRINGS } from '../../config/wagtailConfig';
import Icon from '../Icon/Icon';
const PageCount = ({ page }) => {
interface PageCountProps {
page: {
id: number;
children: {
count: number;
}
}
}
const PageCount: React.FunctionComponent<PageCountProps> = ({ page }) => {
const count = page.children.count;
return (
@ -19,8 +29,4 @@ const PageCount = ({ page }) => {
);
};
PageCount.propTypes = {
page: PropTypes.object.isRequired,
};
export default PageCount;

Wyświetl plik

@ -9,7 +9,7 @@ const getPageFailure = createAction('GET_PAGE_FAILURE', (id, error) => ({ id, er
/**
* Gets a page from the API.
*/
function getPage(id) {
function getPage(id: number) {
return (dispatch) => {
dispatch(getPageStart(id));
@ -28,7 +28,7 @@ const getChildrenFailure = createAction('GET_CHILDREN_FAILURE', (id, error) => (
/**
* Gets the children of a node from the API.
*/
function getChildren(id, offset = 0) {
function getChildren(id: number, offset = 0) {
return (dispatch) => {
dispatch(getChildrenStart(id));
@ -52,7 +52,7 @@ function getChildren(id, offset = 0) {
const openExplorer = createAction('OPEN_EXPLORER', id => ({ id }));
export const closeExplorer = createAction('CLOSE_EXPLORER');
export function toggleExplorer(id) {
export function toggleExplorer(id: number) {
return (dispatch, getState) => {
const { explorer, nodes } = getState();
@ -79,7 +79,7 @@ export function toggleExplorer(id) {
export const popPage = createAction('POP_PAGE');
const pushPagePrivate = createAction('PUSH_PAGE', id => ({ id }));
export function pushPage(id) {
export function pushPage(id: number) {
return (dispatch, getState) => {
const { nodes } = getState();
const page = nodes[id];

Wyświetl plik

@ -1,4 +1,9 @@
const defaultState = {
export interface State {
isVisible: boolean;
path: number[];
}
const defaultState: State = {
isVisible: false,
path: [],
};

Wyświetl plik

@ -1,4 +1,16 @@
const defaultPageState = {
export interface PageState {
isFetching: boolean;
isError: boolean;
children: {
items: any[];
count: number;
};
meta: {
children: any;
};
}
const defaultPageState: PageState = {
isFetching: false,
isError: false,
children: {
@ -50,7 +62,11 @@ const node = (state = defaultPageState, { type, payload }) => {
}
};
const defaultState = {};
export interface State {
[id: number]: PageState;
}
const defaultState: State = {};
/**
* Contains all of the page nodes in one object.

Wyświetl plik

@ -0,0 +1,5 @@
export {};
declare global {
interface Window { __REDUX_DEVTOOLS_EXTENSION__: any; }
}

Wyświetl plik

@ -45,6 +45,7 @@
]
},
"devDependencies": {
"@types/react": "^16.9.53",
"@typescript-eslint/eslint-plugin": "^4.5.0",
"@typescript-eslint/parser": "^4.5.0",
"@wagtail/stylelint-config-wagtail": "^0.1.1",