kopia lustrzana https://github.com/wagtail/wagtail
Convert Explorer into TypeScript
And replace PropTypes with TypeScript annotationspull/6478/head
rodzic
60af9ccef3
commit
650045b573
|
@ -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,
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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),
|
||||
});
|
|
@ -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;
|
|
@ -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];
|
|
@ -1,4 +1,9 @@
|
|||
const defaultState = {
|
||||
export interface State {
|
||||
isVisible: boolean;
|
||||
path: number[];
|
||||
}
|
||||
|
||||
const defaultState: State = {
|
||||
isVisible: false,
|
||||
path: [],
|
||||
};
|
|
@ -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.
|
|
@ -0,0 +1,5 @@
|
|||
export {};
|
||||
|
||||
declare global {
|
||||
interface Window { __REDUX_DEVTOOLS_EXTENSION__: any; }
|
||||
}
|
|
@ -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",
|
||||
|
|
Ładowanie…
Reference in New Issue