Types on src/console

jh-changes
Shin'ya Ueoka 6 years ago
parent 8cf8a0e625
commit 0452370df4
  1. 12
      src/background/usecases/CompletionsUseCase.ts
  2. 34
      src/console/actions/console.ts
  3. 76
      src/console/actions/index.ts
  4. 41
      src/console/components/Console.tsx
  5. 45
      src/console/components/console/Completion.tsx
  6. 9
      src/console/components/console/CompletionItem.tsx
  7. 11
      src/console/components/console/CompletionTitle.tsx
  8. 29
      src/console/components/console/Input.tsx
  9. 13
      src/console/components/console/Message.tsx
  10. 10
      src/console/index.tsx
  11. 23
      src/console/reducers/index.ts
  12. 2
      test/console/actions/console.test.ts
  13. 2
      test/console/reducers/console.test.ts

@ -1,4 +1,3 @@
import Completions from '../domains/Completions';
import CompletionGroup from '../domains/CompletionGroup'; import CompletionGroup from '../domains/CompletionGroup';
import CommandDocs from '../domains/CommandDocs'; import CommandDocs from '../domains/CommandDocs';
import CompletionsRepository from '../repositories/CompletionsRepository'; import CompletionsRepository from '../repositories/CompletionsRepository';
@ -25,7 +24,7 @@ export default class CompletionsUseCase {
this.settingRepository = new SettingRepository(); this.settingRepository = new SettingRepository();
} }
queryConsoleCommand(prefix: string): Promise<Completions> { queryConsoleCommand(prefix: string): Promise<CompletionGroup[]> {
let keys = Object.keys(CommandDocs); let keys = Object.keys(CommandDocs);
let items = keys let items = keys
.filter(name => name.startsWith(prefix)) .filter(name => name.startsWith(prefix))
@ -38,10 +37,10 @@ export default class CompletionsUseCase {
if (items.length === 0) { if (items.length === 0) {
return Promise.resolve([]); return Promise.resolve([]);
} }
return Promise.resolve([{ name: 'Console CompletionGroup', items }]); return Promise.resolve([{ name: 'Console Command', items }]);
} }
async queryOpen(name: string, keywords: string): Promise<Completions> { async queryOpen(name: string, keywords: string): Promise<CompletionGroup[]> {
let settings = await this.settingRepository.get(); let settings = await this.settingRepository.get();
let groups: CompletionGroup[] = []; let groups: CompletionGroup[] = [];
@ -71,7 +70,10 @@ export default class CompletionsUseCase {
} }
// eslint-disable-next-line max-statements // eslint-disable-next-line max-statements
async queryBuffer(name: string, keywords: string): Promise<Completions> { async queryBuffer(
name: string,
keywords: string,
): Promise<CompletionGroup[]> {
let lastId = await this.tabPresenter.getLastSelectedId(); let lastId = await this.tabPresenter.getLastSelectedId();
let trimmed = keywords.trim(); let trimmed = keywords.trim();
let tabs: Tab[] = []; let tabs: Tab[] = [];

@ -1,40 +1,40 @@
import messages from 'shared/messages'; import messages from '../../shared/messages';
import actions from 'console/actions'; import * as actions from './index';
const hide = () => { const hide = (): actions.ConsoleAction => {
return { return {
type: actions.CONSOLE_HIDE, type: actions.CONSOLE_HIDE,
}; };
}; };
const showCommand = (text) => { const showCommand = (text: string): actions.ConsoleAction => {
return { return {
type: actions.CONSOLE_SHOW_COMMAND, type: actions.CONSOLE_SHOW_COMMAND,
text: text text: text
}; };
}; };
const showFind = () => { const showFind = (): actions.ConsoleAction => {
return { return {
type: actions.CONSOLE_SHOW_FIND, type: actions.CONSOLE_SHOW_FIND,
}; };
}; };
const showError = (text) => { const showError = (text: string): actions.ConsoleAction => {
return { return {
type: actions.CONSOLE_SHOW_ERROR, type: actions.CONSOLE_SHOW_ERROR,
text: text text: text
}; };
}; };
const showInfo = (text) => { const showInfo = (text: string): actions.ConsoleAction => {
return { return {
type: actions.CONSOLE_SHOW_INFO, type: actions.CONSOLE_SHOW_INFO,
text: text text: text
}; };
}; };
const hideCommand = () => { const hideCommand = (): actions.ConsoleAction => {
window.top.postMessage(JSON.stringify({ window.top.postMessage(JSON.stringify({
type: messages.CONSOLE_UNFOCUS, type: messages.CONSOLE_UNFOCUS,
}), '*'); }), '*');
@ -43,15 +43,17 @@ const hideCommand = () => {
}; };
}; };
const enterCommand = async(text) => { const enterCommand = async(
text: string,
): Promise<actions.ConsoleAction> => {
await browser.runtime.sendMessage({ await browser.runtime.sendMessage({
type: messages.CONSOLE_ENTER_COMMAND, type: messages.CONSOLE_ENTER_COMMAND,
text, text,
}); });
return hideCommand(text); return hideCommand();
}; };
const enterFind = (text) => { const enterFind = (text: string): actions.ConsoleAction => {
window.top.postMessage(JSON.stringify({ window.top.postMessage(JSON.stringify({
type: messages.CONSOLE_ENTER_FIND, type: messages.CONSOLE_ENTER_FIND,
text, text,
@ -59,14 +61,14 @@ const enterFind = (text) => {
return hideCommand(); return hideCommand();
}; };
const setConsoleText = (consoleText) => { const setConsoleText = (consoleText: string): actions.ConsoleAction => {
return { return {
type: actions.CONSOLE_SET_CONSOLE_TEXT, type: actions.CONSOLE_SET_CONSOLE_TEXT,
consoleText, consoleText,
}; };
}; };
const getCompletions = async(text) => { const getCompletions = async(text: string): Promise<actions.ConsoleAction> => {
let completions = await browser.runtime.sendMessage({ let completions = await browser.runtime.sendMessage({
type: messages.CONSOLE_QUERY_COMPLETIONS, type: messages.CONSOLE_QUERY_COMPLETIONS,
text, text,
@ -78,13 +80,13 @@ const getCompletions = async(text) => {
}; };
}; };
const completionNext = () => { const completionNext = (): actions.ConsoleAction => {
return { return {
type: actions.CONSOLE_COMPLETION_NEXT, type: actions.CONSOLE_COMPLETION_NEXT,
}; };
}; };
const completionPrev = () => { const completionPrev = (): actions.ConsoleAction => {
return { return {
type: actions.CONSOLE_COMPLETION_PREV, type: actions.CONSOLE_COMPLETION_PREV,
}; };
@ -92,5 +94,5 @@ const completionPrev = () => {
export { export {
hide, showCommand, showFind, showError, showInfo, hideCommand, setConsoleText, hide, showCommand, showFind, showError, showInfo, hideCommand, setConsoleText,
enterCommand, enterFind, getCompletions, completionNext, completionPrev enterCommand, enterFind, getCompletions, completionNext, completionPrev,
}; };

@ -1,13 +1,63 @@
export default { // console commands
// console commands export const CONSOLE_HIDE = 'console.hide';
CONSOLE_HIDE: 'console.hide', export const CONSOLE_SHOW_COMMAND = 'console.show.command';
CONSOLE_SHOW_COMMAND: 'console.show.command', export const CONSOLE_SHOW_ERROR = 'console.show.error';
CONSOLE_SHOW_ERROR: 'console.show.error', export const CONSOLE_SHOW_INFO = 'console.show.info';
CONSOLE_SHOW_INFO: 'console.show.info', export const CONSOLE_HIDE_COMMAND = 'console.hide.command';
CONSOLE_HIDE_COMMAND: 'console.hide.command', export const CONSOLE_SET_CONSOLE_TEXT = 'console.set.command';
CONSOLE_SET_CONSOLE_TEXT: 'console.set.command', export const CONSOLE_SET_COMPLETIONS = 'console.set.completions';
CONSOLE_SET_COMPLETIONS: 'console.set.completions', export const CONSOLE_COMPLETION_NEXT = 'console.completion.next';
CONSOLE_COMPLETION_NEXT: 'console.completion.next', export const CONSOLE_COMPLETION_PREV = 'console.completion.prev';
CONSOLE_COMPLETION_PREV: 'console.completion.prev', export const CONSOLE_SHOW_FIND = 'console.show.find';
CONSOLE_SHOW_FIND: 'console.show.find',
}; interface HideAction {
type: typeof CONSOLE_HIDE;
}
interface ShowCommand {
type: typeof CONSOLE_SHOW_COMMAND;
text: string;
}
interface ShowFindAction {
type: typeof CONSOLE_SHOW_FIND;
}
interface ShowErrorAction {
type: typeof CONSOLE_SHOW_ERROR;
text: string;
}
interface ShowInfoAction {
type: typeof CONSOLE_SHOW_INFO;
text: string;
}
interface HideCommandAction {
type: typeof CONSOLE_HIDE_COMMAND;
}
interface SetConsoleTextAction {
type: typeof CONSOLE_SET_CONSOLE_TEXT;
consoleText: string;
}
interface SetCompletionsAction {
type: typeof CONSOLE_SET_COMPLETIONS;
completions: any[];
completionSource: string;
}
interface CompletionNextAction {
type: typeof CONSOLE_COMPLETION_NEXT;
}
interface CompletionPrevAction {
type: typeof CONSOLE_COMPLETION_PREV;
}
export type ConsoleAction =
HideAction | ShowCommand | ShowFindAction | ShowErrorAction |
ShowInfoAction | HideCommandAction | SetConsoleTextAction |
SetCompletionsAction | CompletionNextAction | CompletionPrevAction;

@ -1,7 +1,6 @@
import './console.scss'; import './console.scss';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types';
import Input from './console/Input'; import Input from './console/Input';
import Completion from './console/Completion'; import Completion from './console/Completion';
import Message from './console/Message'; import Message from './console/Message';
@ -9,14 +8,29 @@ import * as consoleActions from '../../console/actions/console';
const COMPLETION_MAX_ITEMS = 33; const COMPLETION_MAX_ITEMS = 33;
class Console extends React.Component { interface Props {
mode?: string;
consoleText?: string;
messageText?: string;
children?: string;
}
class Console extends React.Component<Props> {
private input: HTMLInputElement | null;
constructor(props: Props) {
super(props);
this.input = null;
}
onBlur() { onBlur() {
if (this.props.mode === 'command' || this.props.mode === 'find') { if (this.props.mode === 'command' || this.props.mode === 'find') {
return this.props.dispatch(consoleActions.hideCommand()); return this.props.dispatch(consoleActions.hideCommand());
} }
} }
doEnter(e) { doEnter(e: React.KeyboardEvent<HTMLInputElement>) {
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
@ -28,19 +42,19 @@ class Console extends React.Component {
} }
} }
selectNext(e) { selectNext(e: React.KeyboardEvent<HTMLInputElement>) {
this.props.dispatch(consoleActions.completionNext()); this.props.dispatch(consoleActions.completionNext());
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
} }
selectPrev(e) { selectPrev(e: React.KeyboardEvent<HTMLInputElement>) {
this.props.dispatch(consoleActions.completionPrev()); this.props.dispatch(consoleActions.completionPrev());
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
} }
onKeyDown(e) { onKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {
if (e.keyCode === KeyboardEvent.DOM_VK_ESCAPE && e.ctrlKey) { if (e.keyCode === KeyboardEvent.DOM_VK_ESCAPE && e.ctrlKey) {
this.props.dispatch(consoleActions.hideCommand()); this.props.dispatch(consoleActions.hideCommand());
} }
@ -81,7 +95,7 @@ class Console extends React.Component {
} }
} }
onChange(e) { onChange(e: React.ChangeEvent<HTMLInputElement>) {
let text = e.target.value; let text = e.target.value;
this.props.dispatch(consoleActions.setConsoleText(text)); this.props.dispatch(consoleActions.setConsoleText(text));
if (this.props.mode === 'command') { if (this.props.mode === 'command') {
@ -90,7 +104,7 @@ class Console extends React.Component {
} }
componentDidUpdate(prevProps) { componentDidUpdate(prevProps: Props) {
if (!this.input) { if (!this.input) {
return; return;
} }
@ -134,16 +148,11 @@ class Console extends React.Component {
focus() { focus() {
window.focus(); window.focus();
if (this.input) {
this.input.focus(); this.input.focus();
} }
}
} }
Console.propTypes = { const mapStateToProps = (state: any) => state;
mode: PropTypes.string,
consoleText: PropTypes.string,
messageText: PropTypes.string,
children: PropTypes.string,
};
const mapStateToProps = state => state;
export default connect(mapStateToProps)(Console); export default connect(mapStateToProps)(Console);

@ -1,15 +1,36 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types';
import CompletionItem from './CompletionItem'; import CompletionItem from './CompletionItem';
import CompletionTitle from './CompletionTitle'; import CompletionTitle from './CompletionTitle';
class Completion extends React.Component { interface Item {
constructor() { icon?: string;
super(); caption?: string;
url?: string;
}
interface Group {
name: string;
items: Item[];
}
interface Props {
select: number;
size: number;
completions: Group[];
}
interface State {
viewOffset: number;
select: number;
}
class Completion extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = { viewOffset: 0, select: -1 }; this.state = { viewOffset: 0, select: -1 };
} }
static getDerivedStateFromProps(nextProps, prevState) { static getDerivedStateFromProps(nextProps: Props, prevState: State) {
if (prevState.select === nextProps.select) { if (prevState.select === nextProps.select) {
return null; return null;
} }
@ -24,6 +45,7 @@ class Completion extends React.Component {
} }
index += g.items.length; index += g.items.length;
} }
return -1;
})(); })();
let viewOffset = 0; let viewOffset = 0;
@ -70,17 +92,4 @@ class Completion extends React.Component {
} }
} }
Completion.propTypes = {
select: PropTypes.number,
size: PropTypes.number,
completions: PropTypes.arrayOf(PropTypes.shape({
name: PropTypes.string,
items: PropTypes.arrayOf(PropTypes.shape({
icon: PropTypes.string,
caption: PropTypes.string,
url: PropTypes.string,
})),
})),
};
export default Completion; export default Completion;

@ -1,7 +1,14 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
const CompletionItem = (props) => { interface Props {
highlight: boolean;
caption?: string;
url?: string;
icon?: string;
}
const CompletionItem = (props: Props) => {
let className = 'vimvixen-console-completion-item'; let className = 'vimvixen-console-completion-item';
if (props.highlight) { if (props.highlight) {
className += ' vimvixen-completion-selected'; className += ' vimvixen-completion-selected';

@ -1,14 +1,13 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types';
const CompletionTitle = (props) => { interface Props {
title: string;
}
const CompletionTitle = (props: Props) => {
return <li className='vimvixen-console-completion-title'> return <li className='vimvixen-console-completion-title'>
{props.title} {props.title}
</li>; </li>;
}; };
CompletionTitle.propTypes = {
title: PropTypes.string,
};
export default CompletionTitle; export default CompletionTitle;

@ -1,10 +1,27 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types';
class Input extends React.Component { interface Props {
mode: string;
value: string;
onBlur: (e: React.FocusEvent<Element>) => void;
onKeyDown: (e: React.KeyboardEvent<Element>) => void;
onChange: (e: React.ChangeEvent<Element>) => void;
}
class Input extends React.Component<Props> {
private input: HTMLInputElement | null;
constructor(props: Props) {
super(props);
this.input = null;
}
focus() { focus() {
if (this.input) {
this.input.focus(); this.input.focus();
} }
}
render() { render() {
let prompt = ''; let prompt = '';
@ -32,12 +49,4 @@ class Input extends React.Component {
} }
} }
Input.propTypes = {
mode: PropTypes.string,
value: PropTypes.string,
onBlur: PropTypes.func,
onKeyDown: PropTypes.func,
onChange: PropTypes.func,
};
export default Input; export default Input;

@ -1,7 +1,11 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types';
const Message = (props) => { interface Props {
mode: string;
children: string[];
}
const Message = (props: Props) => {
switch (props.mode) { switch (props.mode) {
case 'error': case 'error':
return ( return (
@ -16,10 +20,7 @@ const Message = (props) => {
</p> </p>
); );
} }
}; return null;
Message.propTypes = {
children: PropTypes.string,
}; };
export default Message; export default Message;

@ -1,8 +1,8 @@
import messages from 'shared/messages'; import messages from '../shared/messages';
import reducers from 'console/reducers'; import reducers from './reducers';
import { createStore, applyMiddleware } from 'redux'; import { createStore, applyMiddleware } from 'redux';
import promise from 'redux-promise'; import promise from 'redux-promise';
import * as consoleActions from 'console/actions/console'; import * as consoleActions from './actions/console';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
import Console from './components/Console'; import Console from './components/Console';
import React from 'react'; import React from 'react';
@ -22,7 +22,7 @@ window.addEventListener('load', () => {
wrapper); wrapper);
}); });
const onMessage = (message) => { const onMessage = (message: any): any => {
switch (message.type) { switch (message.type) {
case messages.CONSOLE_SHOW_COMMAND: case messages.CONSOLE_SHOW_COMMAND:
return store.dispatch(consoleActions.showCommand(message.command)); return store.dispatch(consoleActions.showCommand(message.command));
@ -38,5 +38,5 @@ const onMessage = (message) => {
}; };
browser.runtime.onMessage.addListener(onMessage); browser.runtime.onMessage.addListener(onMessage);
let port = browser.runtime.connect({ name: 'vimvixen-console' }); let port = browser.runtime.connect(undefined, { name: 'vimvixen-console' });
port.onMessage.addListener(onMessage); port.onMessage.addListener(onMessage);

@ -1,4 +1,14 @@
import actions from 'console/actions'; import * as actions from '../actions';
interface State {
mode: string;
messageText: string;
consoleText: string;
completionSource: string;
completions: any[],
select: number;
viewIndex: number;
}
const defaultState = { const defaultState = {
mode: '', mode: '',
@ -10,7 +20,7 @@ const defaultState = {
viewIndex: 0, viewIndex: 0,
}; };
const nextSelection = (state) => { const nextSelection = (state: State): number => {
if (state.completions.length === 0) { if (state.completions.length === 0) {
return -1; return -1;
} }
@ -27,7 +37,7 @@ const nextSelection = (state) => {
return -1; return -1;
}; };
const prevSelection = (state) => { const prevSelection = (state: State): number => {
let length = state.completions let length = state.completions
.map(g => g.items.length) .map(g => g.items.length)
.reduce((x, y) => x + y); .reduce((x, y) => x + y);
@ -37,7 +47,7 @@ const prevSelection = (state) => {
return state.select - 1; return state.select - 1;
}; };
const nextConsoleText = (completions, select, defaults) => { const nextConsoleText = (completions: any[], select: number, defaults: any) => {
if (select < 0) { if (select < 0) {
return defaults; return defaults;
} }
@ -46,7 +56,10 @@ const nextConsoleText = (completions, select, defaults) => {
}; };
// eslint-disable-next-line max-lines-per-function // eslint-disable-next-line max-lines-per-function
export default function reducer(state = defaultState, action = {}) { export default function reducer(
state: State = defaultState,
action: actions.ConsoleAction,
): State {
switch (action.type) { switch (action.type) {
case actions.CONSOLE_HIDE: case actions.CONSOLE_HIDE:
return { ...state, return { ...state,

@ -1,4 +1,4 @@
import actions from 'console/actions'; import * as actions from 'console/actions';
import * as consoleActions from 'console/actions/console'; import * as consoleActions from 'console/actions/console';
describe("console actions", () => { describe("console actions", () => {

@ -1,4 +1,4 @@
import actions from 'console/actions'; import * as actions from 'console/actions';
import reducer from 'console/reducers'; import reducer from 'console/reducers';
describe("console reducer", () => { describe("console reducer", () => {