console as redux architecture

jh-changes
Shin'ya Ueoka 7 years ago
parent e1c70769ea
commit 956dd937d3
  1. 14
      src/console/actions/console.js
  2. 1
      src/console/actions/index.js
  3. 131
      src/console/components/console.js
  4. 3
      src/console/index.html
  5. 19
      src/console/reducers/index.js
  6. 14
      test/console/actions/console.test.js
  7. 10
      test/console/reducers/console.test.js

@ -33,10 +33,18 @@ const hideCommand = () => {
};
};
const setCompletions = (completions) => {
const setConsoleText = (consoleText) => {
return {
type: actions.CONSOLE_SET_CONSOLE_TEXT,
consoleText,
};
};
const setCompletions = (completionSource, completions) => {
return {
type: actions.CONSOLE_SET_COMPLETIONS,
completions: completions
completionSource,
completions,
};
};
@ -53,6 +61,6 @@ const completionPrev = () => {
};
export {
showCommand, showFind, showError, showInfo, hideCommand,
showCommand, showFind, showError, showInfo, hideCommand, setConsoleText,
setCompletions, completionNext, completionPrev
};

@ -4,6 +4,7 @@ export default {
CONSOLE_SHOW_ERROR: 'console.show.error',
CONSOLE_SHOW_INFO: 'console.show.info',
CONSOLE_HIDE_COMMAND: 'console.hide.command',
CONSOLE_SET_CONSOLE_TEXT: 'console.set.command',
CONSOLE_SET_COMPLETIONS: 'console.set.completions',
CONSOLE_COMPLETION_NEXT: 'console.completion.next',
CONSOLE_COMPLETION_PREV: 'console.completion.prev',

@ -1,25 +1,27 @@
import messages from 'shared/messages';
import * as consoleActions from 'console/actions/console';
const inputShownMode = (state) => {
return ['command', 'find'].includes(state.mode);
};
export default class ConsoleComponent {
constructor(wrapper, store) {
this.wrapper = wrapper;
this.prevState = {};
this.completionOrigin = '';
this.store = store;
this.prevMode = '';
let doc = this.wrapper.ownerDocument;
let input = doc.querySelector('#vimvixen-console-command-input');
input.addEventListener('blur', this.onBlur.bind(this));
input.addEventListener('keydown', this.onKeyDown.bind(this));
input.addEventListener('input', this.onInput.bind(this));
this.hideCommand();
this.hideMessage();
store.subscribe(() => {
this.update();
});
this.update();
}
onBlur() {
@ -53,105 +55,80 @@ export default class ConsoleComponent {
}
onInput(e) {
let doc = this.wrapper.ownerDocument;
let input = doc.querySelector('#vimvixen-console-command-input');
this.completionOrigin = input.value;
this.store.dispatch(consoleActions.setConsoleText(e.target.value));
let source = e.target.value;
return browser.runtime.sendMessage({
type: messages.CONSOLE_QUERY_COMPLETIONS,
text: e.target.value,
text: source,
}).then((completions) => {
this.store.dispatch(consoleActions.setCompletions(completions));
this.store.dispatch(consoleActions.setCompletions(source, completions));
});
}
update() {
let state = this.store.getState();
if (this.prevState.mode !== 'command' && state.mode === 'command') {
this.showCommand(state.consoleText);
} else if (this.prevState.mode !== 'find' && state.mode === 'find') {
this.showFind();
} else if (state.mode !== 'command' && state.mode !== 'find') {
this.hideCommand();
}
onInputShown(state) {
let doc = this.wrapper.ownerDocument;
let input = doc.querySelector('#vimvixen-console-command-input');
if (state.mode === 'error' || state.mode === 'info') {
this.showMessage(state.mode, state.messageText);
} else {
this.hideMessage();
}
input.focus();
window.focus();
if (state.groupSelection >= 0 && state.itemSelection >= 0) {
let group = state.completions[state.groupSelection];
let item = group.items[state.itemSelection];
this.setCommandValue(item.content);
} else if (state.completions.length > 0 &&
JSON.stringify(this.prevState.completions) ===
JSON.stringify(state.completions)) {
// Reset input only completion groups not changed (unselected an item in
// completion) in order to avoid to override previous input
this.setCommandCompletionOrigin();
if (state.mode === 'command') {
this.onInput({ target: input });
}
this.prevState = state;
}
showCommand(text) {
this.showConsole('command', text);
}
update() {
let state = this.store.getState();
this.updateMessage(state);
this.updateCommand(state);
this.updatePrompt(state);
showFind() {
this.showConsole('find', '');
if (this.prevMode !== state.mode && inputShownMode(state)) {
this.onInputShown(state);
}
this.prevMode = state.mode;
}
showConsole(mode, initial) {
updateMessage(state) {
let doc = this.wrapper.ownerDocument;
let command = doc.querySelector('#vimvixen-console-command');
let input = doc.querySelector('#vimvixen-console-command-input');
let promptEle = doc.querySelector('.vimvixen-console-command-prompt');
let box = doc.querySelector('.vimvixen-console-message');
let display = 'none';
let classList = ['vimvixen-console-message'];
command.style.display = 'block';
input.value = initial;
input.focus();
promptEle.className = `vimvixen-console-command-prompt prompt-${mode}`;
if (state.mode === 'error' || state.mode === 'info') {
display = 'block';
classList.push('vimvixen-console-' + state.mode);
}
window.focus();
this.onInput({ target: input });
box.className = classList.join(' ');
box.style.display = display;
box.textContent = state.messageText;
}
hideCommand() {
updateCommand(state) {
let doc = this.wrapper.ownerDocument;
let command = doc.querySelector('#vimvixen-console-command');
command.style.display = 'none';
}
setCommandValue(value) {
let doc = this.wrapper.ownerDocument;
let input = doc.querySelector('#vimvixen-console-command-input');
input.value = value;
}
setCommandCompletionOrigin() {
let doc = this.wrapper.ownerDocument;
let input = doc.querySelector('#vimvixen-console-command-input');
input.value = this.completionOrigin;
}
let display = 'none';
if (inputShownMode(state)) {
display = 'block';
}
showMessage(mode, text) {
let doc = this.wrapper.ownerDocument;
let error = doc.querySelector('#vimvixen-console-message');
error.classList.remove(
'vimvixen-console-info',
'vimvixen-console-error'
);
error.classList.add('vimvixen-console-' + mode);
error.textContent = text;
error.style.display = 'block';
command.style.display = display;
input.value = state.consoleText;
}
hideMessage() {
updatePrompt(state) {
let classList = ['vimvixen-console-command-prompt'];
if (inputShownMode(state)) {
classList.push('prompt-' + state.mode);
}
let doc = this.wrapper.ownerDocument;
let error = doc.querySelector('#vimvixen-console-message');
error.style.display = 'none';
let ele = doc.querySelector('.vimvixen-console-command-prompt');
ele.className = classList.join(' ');
}
}

@ -6,8 +6,7 @@
<script src='console.js'></script>
</head>
<body class='vimvixen-console'>
<p id='vimvixen-console-message'
class='vimvixen-console-message'></p>
<p class='vimvixen-console-message'></p>
<div id='vimvixen-console-command'>
<ul id='vimvixen-console-completion' class='vimvixen-console-completion'></ul>
<div class='vimvixen-console-command'>

@ -4,6 +4,7 @@ const defaultState = {
mode: '',
messageText: '',
consoleText: '',
completionSource: '',
completions: [],
groupSelection: -1,
itemSelection: -1,
@ -43,6 +44,13 @@ const prevSelection = (state) => {
return [state.groupSelection, state.itemSelection - 1];
};
const nextConsoleText = (completions, group, item, defaults) => {
if (group < 0 || item < 0) {
return defaults;
}
return completions[group].items[item].content;
};
export default function reducer(state = defaultState, action = {}) {
switch (action.type) {
case actions.CONSOLE_SHOW_COMMAND:
@ -71,9 +79,14 @@ export default function reducer(state = defaultState, action = {}) {
return Object.assign({}, state, {
mode: state.mode === 'command' || state.mode === 'find' ? '' : state.mode,
});
case actions.CONSOLE_SET_CONSOLE_TEXT:
return Object.assign({}, state, {
consoleText: action.consoleText,
});
case actions.CONSOLE_SET_COMPLETIONS:
return Object.assign({}, state, {
completions: action.completions,
completionSource: action.completionSource,
groupSelection: -1,
itemSelection: -1,
});
@ -82,6 +95,9 @@ export default function reducer(state = defaultState, action = {}) {
return Object.assign({}, state, {
groupSelection: next[0],
itemSelection: next[1],
consoleText: nextConsoleText(
state.completions, next[0], next[1],
state.completionSource),
});
}
case actions.CONSOLE_COMPLETION_PREV: {
@ -89,6 +105,9 @@ export default function reducer(state = defaultState, action = {}) {
return Object.assign({}, state, {
groupSelection: next[0],
itemSelection: next[1],
consoleText: nextConsoleText(
state.completions, next[0], next[1],
state.completionSource),
});
}
default:

@ -34,17 +34,26 @@ describe("console actions", () => {
});
});
describe("hide", () => {
describe("hideCommand", () => {
it('create CONSOLE_HIDE_COMMAND action', () => {
let action = consoleActions.hideCommand();
expect(action.type).to.equal(actions.CONSOLE_HIDE_COMMAND);
});
});
describe('setConsoleText', () => {
it('create CONSOLE_SET_CONSOLE_TEXT action', () => {
let action = consoleActions.setConsoleText('hello world');
expect(action.type).to.equal(actions.CONSOLE_SET_CONSOLE_TEXT);
expect(action.consoleText).to.equal('hello world');
});
});
describe("setCompletions", () => {
it('create CONSOLE_SET_COMPLETIONS action', () => {
let action = consoleActions.setCompletions([1,2,3]);
let action = consoleActions.setCompletions('query', [1, 2, 3]);
expect(action.type).to.equal(actions.CONSOLE_SET_COMPLETIONS);
expect(action.completionSource).to.deep.equal('query');
expect(action.completions).to.deep.equal([1, 2, 3]);
});
});
@ -63,4 +72,3 @@ describe("console actions", () => {
});
});
});

@ -43,6 +43,16 @@ describe("console reducer", () => {
expect(state).to.have.property('mode', 'error');
});
it('return next state for CONSOLE_SET_CONSOLE_TEXT', () => {
let action = {
type: actions.CONSOLE_SET_CONSOLE_TEXT,
consoleText: 'hello world'
}
let state = reducer({}, action)
expect(state).to.have.property('consoleText', 'hello world');
});
it ('return next state for CONSOLE_SET_COMPLETIONS', () => {
let state = {
groupSelection: 0,