You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ga...@apache.org on 2016/11/07 08:33:45 UTC
[2/3] fauxton commit: updated refs/heads/master to 369c326
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/369c3265/app/addons/components/react-components.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/components/react-components.react.jsx b/app/addons/components/react-components.react.jsx
index 5b4a396..be98c91 100644
--- a/app/addons/components/react-components.react.jsx
+++ b/app/addons/components/react-components.react.jsx
@@ -10,1623 +10,46 @@
// License for the specific language governing permissions and limitations under
// the License.
-import app from "../../app";
-import FauxtonAPI from "../../core/api";
-import React from "react";
-import ReactDOM from "react-dom";
-import Stores from "./stores";
-import Actions from "./actions";
-import FauxtonComponents from "../fauxton/components.react";
-import Helpers from "../documents/helpers";
-import beautifyHelper from "../../../assets/js/plugins/beautify";
-import {Modal, Popover, OverlayTrigger} from "react-bootstrap";
-import ReactCSSTransitionGroup from "react-addons-css-transition-group";
-import ace from "brace";
-
-const { componentStore } = Stores;
-
-var BadgeList = React.createClass({
-
- propTypes: {
- elements: React.PropTypes.array.isRequired,
- removeBadge: React.PropTypes.func.isRequired
- },
-
- getDefaultProps: function () {
- return {
- getLabel: function (el) {
- return el;
- },
-
- getId: function (el) {
- return el;
- }
-
- };
- },
-
- getBadges: function () {
- return this.props.elements.map(function (el, i) {
- return <Badge
- label={this.props.getLabel(el)}
- key={i}
- id={el}
- remove={this.removeBadge} />;
- }.bind(this));
- },
-
- removeBadge: function (label, el) {
- this.props.removeBadge(label, el);
- },
-
- render: function () {
- return (
- <ul className="component-badgelist">
- {this.getBadges()}
- </ul>
- );
- }
-});
-
-var Badge = React.createClass({
- propTypes: {
- label: React.PropTypes.string.isRequired,
- remove: React.PropTypes.func.isRequired
- },
-
- remove: function (e) {
- e.preventDefault();
- this.props.remove(this.props.label, this.props.id);
- },
-
- render: function () {
- return (
- <li className="component-badge">
- <span className="label label-info">{this.props.label}</span>
- <a
- href="#"
- className="label label-info remove-filter"
- onClick={this.remove} data-bypass="true"
- >
- ×
- </a>
- </li>
- );
- }
-});
-
-var ToggleHeaderButton = React.createClass({
- getDefaultProps: function () {
- return {
- innerClasses: '',
- fonticon: '',
- containerClasses: '',
- selected: false,
- title: '',
- disabled: false,
- toggleCallback: null,
- text: '',
- iconDefaultClass: 'icon'
- };
- },
-
- render: function () {
- const { iconDefaultClass, fonticon, innerClasses, selected, containerClasses, title, disabled, text, toggleCallback } = this.props;
- const selectedBtnClass = (selected) ? 'js-headerbar-togglebutton-selected' : '';
-
- return (
- <button
- title={title}
- disabled={disabled}
- onClick={toggleCallback}
- className={`button ${containerClasses} ${selectedBtnClass}`}
- >
- <i className={`${iconDefaultClass} ${fonticon} ${innerClasses}`}></i><span>{text}</span>
- </button>
- );
- }
-});
-
-
-var BulkActionComponent = React.createClass({
-
- propTypes: {
- hasSelectedItem: React.PropTypes.bool.isRequired,
- removeItem: React.PropTypes.func.isRequired,
- selectAll: React.PropTypes.func,
- toggleSelect: React.PropTypes.func.isRequired,
- isChecked: React.PropTypes.bool.isRequired,
- disabled: React.PropTypes.bool
- },
-
- getDefaultProps: function () {
- return {
- disabled: false,
- title: 'Select rows that can be...',
- bulkIcon: 'fonticon-trash',
- buttonTitle: 'Delete all selected',
- dropdownContentText: 'Deleted',
- enableOverlay: false
- };
- },
-
- render: function () {
- return (
- <div className="bulk-action-component">
- <div className="bulk-action-component-selector-group">
- {this.getMasterSelector()}
- {this.getMultiSelectOptions()}
- </div>
- </div>
- );
- },
-
- getMultiSelectOptions: function () {
- if (!this.props.hasSelectedItem) {
- return null;
- }
-
- return (
- <button
- onClick={this.props.removeItem}
- className={'fonticon ' + this.props.bulkIcon}
- title={this.props.buttonTitle} />
- );
- },
-
- getPopupContent: function () {
- return (
- <ul className="bulk-action-component-popover-actions">
- <li onClick={this.selectAll} >
- <i className="icon fonticon-cancel"></i> {this.props.dropdownContentText}
- </li>
- </ul>
- );
- },
-
- selectAll: function () {
- this.refs.bulkActionPopover.hide();
- this.props.selectAll();
- },
-
- getOverlay: function () {
- return (
- <OverlayTrigger
- ref="bulkActionPopover"
- trigger="click"
- placement="bottom"
- rootClose={true}
- overlay={
- <Popover id="bulk-action-component-popover" title={this.props.title}>
- {this.getPopupContent()}
- </Popover>
- }>
- <div className="arrow-button">
- <i className="fonticon fonticon-play"></i>
- </div>
- </OverlayTrigger>
- );
- },
-
- getMasterSelector: function () {
- return (
- <div className="bulk-action-component-panel">
- <input type="checkbox"
- checked={this.props.isChecked}
- onChange={this.props.toggleSelect}
- disabled={this.props.disabled} />
- {this.props.enableOverlay ? <div className="separator"></div> : null}
- {this.props.enableOverlay ? this.getOverlay() : null}
- </div>
- );
- },
-
-});
-
-var StyledSelect = React.createClass({
- propTypes: {
- selectValue: React.PropTypes.string.isRequired,
- selectId: React.PropTypes.string.isRequired,
- selectChange: React.PropTypes.func.isRequired
- },
-
- render: function () {
- return (
- <div className="styled-select">
- <label htmlFor={this.props.selectId}>
- <i className="fonticon-down-dir"></i>
- <select
- value={this.props.selectValue}
- id={this.props.selectId}
- className={this.props.selectValue}
- onChange={this.props.selectChange}
- >
- {this.props.selectContent}
- </select>
- </label>
- </div>
- );
- }
-});
-
-
-/**
- * A pre-packaged JS editor panel for use on the Edit Index / Mango pages. Includes options for a title, zen mode
- * icon and beautify button.
- */
-var CodeEditorPanel = React.createClass({
- getDefaultProps: function () {
- return {
- id: 'code-editor',
- className: '',
- defaultCode: '',
- title: '',
- docLink: '',
- allowZenMode: true,
- blur: function () {}
- };
- },
-
- getInitialState: function () {
- return this.getStoreState();
- },
-
- getStoreState: function () {
- return {
- zenModeEnabled: false,
- code: this.props.defaultCode
- };
- },
-
- componentWillReceiveProps: function (nextProps) {
- if (nextProps.defaultCode !== this.props.defaultCode) {
- this.setState({ code: nextProps.defaultCode });
- }
- },
-
- // list of JSHINT errors to ignore: gets around problem of anonymous functions not being valid
- ignorableErrors: [
- 'Missing name in function declaration.',
- "['{a}'] is better written in dot notation."
- ],
-
- getZenModeIcon: function () {
- if (this.props.allowZenMode) {
- return <span className="fonticon fonticon-resize-full zen-editor-icon" title="Enter Zen mode" onClick={this.enterZenMode}></span>;
- }
- },
-
- getDocIcon: function () {
- if (this.props.docLink) {
- return (
- <a className="help-link"
- data-bypass="true"
- href={this.props.docLink}
- target="_blank"
- >
- <i className="icon-question-sign"></i>
- </a>
- );
- }
- },
-
- getZenModeOverlay: function () {
- if (this.state.zenModeEnabled) {
- return (
- <ZenModeOverlay
- defaultCode={this.state.code}
- mode={this.props.mode}
- ignorableErrors={this.ignorableErrors}
- onExit={this.exitZenMode} />
- );
- }
- },
-
- enterZenMode: function () {
- this.setState({
- zenModeEnabled: true,
- code: this.refs.codeEditor.getValue()
- });
- },
-
- exitZenMode: function (content) {
- this.setState({ zenModeEnabled: false });
- this.getEditor().setValue(content);
- },
-
- getEditor: function () {
- return this.refs.codeEditor;
- },
-
- getValue: function () {
- return this.getEditor().getValue();
- },
-
- beautify: function (code) {
- this.setState({ code: code });
- this.getEditor().setValue(code);
- },
-
- update: function () {
- this.getEditor().setValue(this.state.code);
- },
-
- render: function () {
- var classes = 'control-group';
- if (this.props.className) {
- classes += ' ' + this.props.className;
- }
- return (
- <div className={classes}>
- <label>
- <span>{this.props.title}</span>
- {this.getDocIcon()}
- {this.getZenModeIcon()}
- </label>
- <CodeEditor
- id={this.props.id}
- ref="codeEditor"
- mode="javascript"
- defaultCode={this.state.code}
- showGutter={true}
- ignorableErrors={this.ignorableErrors}
- setHeightToLineCount={true}
- blur={this.props.blur}
- />
- <Beautify code={this.state.code} beautifiedCode={this.beautify} />
- {this.getZenModeOverlay()}
- </div>
- );
- }
-});
-
-
-require('brace/mode/javascript');
-require('brace/mode/json');
-require('brace/theme/idle_fingers');
-
-var CodeEditor = React.createClass({
- getDefaultProps: function () {
- return {
- id: 'code-editor',
- mode: 'javascript',
- theme: 'idle_fingers',
- fontSize: 13,
-
- // this sets the default value for the editor. On the fly changes are stored in state in this component only. To
- // change the editor content after initial construction use CodeEditor.setValue()
- defaultCode: '',
-
- showGutter: true,
- highlightActiveLine: true,
- showPrintMargin: false,
- autoScrollEditorIntoView: true,
- autoFocus: false,
- stringEditModalEnabled: false,
-
- // these two options create auto-resizeable code editors, with a maximum number of lines
- setHeightToLineCount: false,
- maxLines: 10,
-
- // optional editor key commands (e.g. specific save action)
- editorCommands: [],
-
- // notifies users that there is unsaved changes in the editor when navigating away from the page
- notifyUnsavedChanges: false,
-
- // an optional array of ignorable Ace errors. Lets us filter out errors based on context
- ignorableErrors: [],
-
- // un-Reacty, but the code editor is a self-contained component and it's helpful to be able to tie into
- // editor specific events like content changes and leaving the editor
- change: function () {},
- blur: function () {}
- };
- },
-
- getInitialState: function () {
- return {
- originalCode: this.props.defaultCode,
-
- // these are all related to the (optional) string edit modal
- stringEditModalVisible: false,
- stringEditIconVisible: false,
- stringEditIconStyle: {},
- stringEditModalValue: ''
- };
- },
-
- hasChanged: function () {
- return !_.isEqual(this.state.originalCode, this.getValue());
- },
-
- clearChanges: function () {
- this.setState({
- originalCode: this.getValue()
- });
- },
-
- setupAce: function (props, shouldUpdateCode) {
- this.editor = ace.edit(ReactDOM.findDOMNode(this.refs.ace));
-
- // suppresses an Ace editor error
- this.editor.$blockScrolling = Infinity;
-
- if (shouldUpdateCode) {
- this.setValue(props.defaultCode);
- }
-
- this.editor.setShowPrintMargin(props.showPrintMargin);
- this.editor.autoScrollEditorIntoView = props.autoScrollEditorIntoView;
-
- this.editor.setOption('highlightActiveLine', this.props.highlightActiveLine);
-
- if (this.props.setHeightToLineCount) {
- this.setHeightToLineCount();
- }
-
- if (this.props.ignorableErrors) {
- this.removeIgnorableAnnotations();
- }
-
- this.addCommands();
- this.editor.getSession().setMode('ace/mode/' + props.mode);
- this.editor.setTheme('ace/theme/' + props.theme);
- this.editor.setFontSize(props.fontSize);
- this.editor.getSession().setTabSize(2);
- this.editor.getSession().setUseSoftTabs(true);
-
- if (this.props.autoFocus) {
- this.editor.focus();
- }
- },
-
- addCommands: function () {
- _.each(this.props.editorCommands, function (command) {
- this.editor.commands.addCommand(command);
- }, this);
- },
-
- setupEvents: function () {
- this.editor.on('blur', _.bind(this.onBlur, this));
- this.editor.on('change', _.bind(this.onContentChange, this));
-
- if (this.props.stringEditModalEnabled) {
- this.editor.on('changeSelection', _.bind(this.showHideEditStringGutterIcon, this));
- this.editor.getSession().on('changeBackMarker', _.bind(this.showHideEditStringGutterIcon, this));
- this.editor.getSession().on('changeScrollTop', _.bind(this.updateEditStringGutterIconPosition, this));
- }
-
- if (this.props.notifyUnsavedChanges) {
- $(window).on('beforeunload.editor_' + this.props.id, _.bind(this.quitWarningMsg));
- FauxtonAPI.beforeUnload('editor_' + this.props.id, _.bind(this.quitWarningMsg, this));
- }
- },
-
- onBlur: function () {
- this.props.blur(this.getValue());
- },
-
- onContentChange: function () {
- if (this.props.setHeightToLineCount) {
- this.setHeightToLineCount();
- }
- this.props.change(this.getValue());
- },
-
- quitWarningMsg: function () {
- if (this.hasChanged()) {
- return 'Your changes have not been saved. Click Cancel to return to the document, or OK to proceed.';
- }
- },
-
- removeEvents: function () {
- if (this.props.notifyUnsavedChanges) {
- $(window).off('beforeunload.editor_' + this.props.id);
- FauxtonAPI.removeBeforeUnload('editor_' + this.props.id);
- }
- },
-
- setHeightToLineCount: function () {
- var numLines = this.editor.getSession().getDocument().getLength();
- var maxLines = (numLines > this.props.maxLines) ? this.props.maxLines : numLines;
- this.editor.setOptions({
- maxLines: maxLines
- });
- },
-
- componentDidMount: function () {
- this.setupAce(this.props, true);
- this.setupEvents();
-
- if (this.props.autoFocus) {
- this.editor.focus();
- }
- },
-
- componentWillUnmount: function () {
- this.removeEvents();
- this.editor.destroy();
- },
-
- componentWillReceiveProps: function (nextProps) {
- this.setupAce(nextProps, false);
- },
-
- getAnnotations: function () {
- return this.editor.getSession().getAnnotations();
- },
-
- isIgnorableError: function (msg) {
- return _.contains(this.props.ignorableErrors, msg);
- },
-
- removeIgnorableAnnotations: function () {
- var isIgnorableError = this.isIgnorableError;
- this.editor.getSession().on('changeAnnotation', function () {
- var annotations = this.editor.getSession().getAnnotations();
- var newAnnotations = _.reduce(annotations, function (annotations, error) {
- if (!isIgnorableError(error.raw)) {
- annotations.push(error);
- }
- return annotations;
- }, []);
-
- if (annotations.length !== newAnnotations.length) {
- this.editor.getSession().setAnnotations(newAnnotations);
- }
- }.bind(this));
- },
-
- showHideEditStringGutterIcon: function (e) {
- if (this.hasErrors() || !this.parseLineForStringMatch()) {
- this.setState({ stringEditIconVisible: false });
- return false;
- }
-
- this.setState({
- stringEditIconVisible: true,
- stringEditIconStyle: {
- top: this.getGutterIconPosition()
- }
- });
-
- return true;
- },
-
- updateEditStringGutterIconPosition: function () {
- if (!this.state.stringEditIconVisible) {
- return;
- }
- this.setState({
- stringEditIconStyle: {
- top: this.getGutterIconPosition()
- }
- });
- },
-
- getGutterIconPosition: function () {
- var rowHeight = this.getRowHeight();
- var scrollTop = this.editor.session.getScrollTop();
- var positionFromTop = (rowHeight * this.documentToScreenRow(this.getSelectionStart().row)) - scrollTop;
- return positionFromTop + 'px';
- },
-
- parseLineForStringMatch: function () {
- var selStart = this.getSelectionStart().row;
- var selEnd = this.getSelectionEnd().row;
-
- // one JS(ON) string can't span more than one line - we edit one string, so ensure we don't select several lines
- if (selStart >= 0 && selEnd >= 0 && selStart === selEnd && this.isRowExpanded(selStart)) {
- var editLine = this.getLine(selStart),
- editMatch = editLine.match(/^([ \t]*)("[a-zA-Z0-9_]*["|']: )?(["|'].*",?[ \t]*)$/);
-
- if (editMatch) {
- return editMatch;
- }
- }
- return false;
- },
-
- openStringEditModal: function () {
- var matches = this.parseLineForStringMatch();
- var string = matches[3];
- var lastChar = string.length - 1;
- if (string.substring(string.length - 1) === ',') {
- lastChar = string.length - 2;
- }
- string = string.substring(1, lastChar);
-
- this.setState({
- stringEditModalVisible: true,
- stringEditModalValue: string
- });
- },
-
- saveStringEditModal: function (newString) {
- // replace the string on the selected line
- var line = this.parseLineForStringMatch();
- var indent = line[1] || '',
- key = line[2] || '',
- originalString = line[3],
- comma = '';
- if (originalString.substring(originalString.length - 1) === ',') {
- comma = ',';
- }
- this.replaceCurrentLine(indent + key + JSON.stringify(newString) + comma + '\n');
- this.closeStringEditModal();
- },
-
- closeStringEditModal: function () {
- this.setState({
- stringEditModalVisible: false
- });
- },
-
- hasErrors: function () {
- return !_.every(this.getAnnotations(), function (error) {
- return this.isIgnorableError(error.raw);
- }, this);
- },
-
- setReadOnly: function (readonly) {
- this.editor.setReadOnly(readonly);
- },
-
- setValue: function (code, lineNumber) {
- lineNumber = lineNumber ? lineNumber : -1;
- this.editor.setValue(code, lineNumber);
- },
-
- getValue: function () {
- return this.editor.getValue();
- },
-
- getEditor: function () {
- return this;
- },
-
- getLine: function (lineNum) {
- return this.editor.session.getLine(lineNum);
- },
-
- getSelectionStart: function () {
- return this.editor.getSelectionRange().start;
- },
-
- getSelectionEnd: function () {
- return this.editor.getSelectionRange().end;
- },
-
- getRowHeight: function () {
- return this.editor.renderer.layerConfig.lineHeight;
- },
-
- isRowExpanded: function (row) {
- return !this.editor.getSession().isRowFolded(row);
- },
-
- documentToScreenRow: function (row) {
- return this.editor.getSession().documentToScreenRow(row, 0);
- },
-
- replaceCurrentLine: function (replacement) {
- this.editor.getSelection().selectLine();
- this.editor.insert(replacement);
- this.editor.getSelection().moveCursorUp();
- },
-
- render: function () {
- return (
- <div>
- <div ref="ace" className="js-editor" id={this.props.id}></div>
- <button ref="stringEditIcon" className="btn string-edit" title="Edit string" disabled={!this.state.stringEditIconVisible}
- style={this.state.stringEditIconStyle} onClick={this.openStringEditModal}>
- <i className="icon icon-edit"></i>
- </button>
- <StringEditModal
- ref="stringEditModal"
- visible={this.state.stringEditModalVisible}
- value={this.state.stringEditModalValue}
- onSave={this.saveStringEditModal}
- onClose={this.closeStringEditModal} />
- </div>
- );
- }
-});
-
-
-// this appears when the cursor is over a string. It shows an icon in the gutter that opens the modal.
-var StringEditModal = React.createClass({
-
- propTypes: {
- value: React.PropTypes.string.isRequired,
- visible: React.PropTypes.bool.isRequired,
- onClose: React.PropTypes.func.isRequired,
- onSave: React.PropTypes.func.isRequired
- },
-
- getDefaultProps: function () {
- return {
- visible: false,
- onClose: function () { },
- onSave: function () { }
- };
- },
-
- componentDidMount: function () {
- if (!this.props.visible) {
- return;
- }
- this.initEditor(this.props.value);
- },
-
- componentDidUpdate: function (prevProps) {
- if (!this.props.visible) {
- return;
- }
- var val = '';
- if (!prevProps.visible && this.props.visible) {
- val = Helpers.parseJSON(this.props.value);
- }
-
- this.initEditor(val);
- },
-
- initEditor: function (val) {
- this.editor = ace.edit(ReactDOM.findDOMNode(this.refs.stringEditor));
- this.editor.$blockScrolling = Infinity; // suppresses an Ace editor error
- this.editor.setShowPrintMargin(false);
- this.editor.setOption('highlightActiveLine', true);
- this.editor.setTheme('ace/theme/idle_fingers');
- this.editor.setValue(val, -1);
- },
-
- closeModal: function () {
- this.props.onClose();
- },
-
- save: function () {
- this.props.onSave(this.editor.getValue());
- },
-
- render: function () {
- return (
- <Modal dialogClassName="string-editor-modal" show={this.props.visible} onHide={this.closeModal}>
- <Modal.Header closeButton={true}>
- <Modal.Title>Edit Value <span id="string-edit-header"></span></Modal.Title>
- </Modal.Header>
- <Modal.Body>
- <div id="modal-error" className="hide alert alert-error"/>
- <div id="string-editor-wrapper"><div ref="stringEditor" className="doc-code"></div></div>
- </Modal.Body>
- <Modal.Footer>
- <a className="cancel-link" onClick={this.closeModal}>Cancel</a>
- <button id="string-edit-save-btn" onClick={this.save} className="btn btn-success save">
- <i className="fonticon-circle-check"></i> Modify Text
- </button>
- </Modal.Footer>
- </Modal>
- );
- }
-});
-
-
-// Zen mode editing has very few options:
-// - It covers the full screen, hiding everything else
-// - Two themes: light & dark (choice stored in local storage)
-// - No save option, but has a 1-1 map with a <CodeEditor /> element which gets updated when the user leaves
-// - [Escape] closes the mode, as does clicking the shrink icon at the top right
-var ZenModeOverlay = React.createClass({
- getDefaultProps: function () {
- return {
- mode: 'javascript',
- defaultCode: '',
- ignorableErrors: [],
- onExit: null,
- highlightActiveLine: false
- };
- },
-
- themes: {
- dark: 'idle_fingers',
- light: 'dawn'
- },
-
- getInitialState: function () {
- return this.getStoreState();
- },
-
- getStoreState: function () {
- return {
- theme: this.getZenTheme(),
- code: this.props.defaultCode
- };
- },
-
- getZenTheme: function () {
- var selectedTheme = app.utils.localStorageGet('zenTheme');
- return _.isUndefined(selectedTheme) ? 'dark' : selectedTheme;
- },
-
- onChange: function () {
- this.setState(this.getStoreState());
- },
-
- componentDidMount: function () {
- $(ReactDOM.findDOMNode(this.refs.exit)).tooltip({ placement: 'left' });
- $(ReactDOM.findDOMNode(this.refs.theme)).tooltip({ placement: 'left' });
- },
-
- exitZenMode: function () {
- this.props.onExit(this.getValue());
- },
-
- getValue: function () {
- return this.refs.ace.getValue();
- },
-
- toggleTheme: function () {
- var newTheme = (this.state.theme === 'dark') ? 'light' : 'dark';
- this.setState({
- theme: newTheme,
- code: this.getValue()
- });
- app.utils.localStorageSet('zenTheme', newTheme);
- },
-
- setValue: function (code, lineNumber) {
- lineNumber = lineNumber ? lineNumber : -1;
- this.editor.setValue(code, lineNumber);
- },
-
- render: function () {
- var classes = 'full-page-editor-modal-wrapper zen-theme-' + this.state.theme;
-
- var editorCommands = [{
- name: 'close',
- bindKey: { win: 'ESC', mac: 'ESC' },
- exec: this.exitZenMode
- }];
-
- return (
- <div className={classes}>
- <div className="zen-mode-controls">
- <ul>
- <li>
- <span ref="exit"
- className="fonticon fonticon-resize-small js-exit-zen-mode"
- data-toggle="tooltip"
- data-container=".zen-mode-controls .tooltips"
- title="Exit zen mode (`esc`)"
- onClick={this.exitZenMode}></span>
- </li>
- <li>
- <span ref="theme"
- className="fonticon fonticon-picture js-toggle-theme"
- data-toggle="tooltip"
- data-container=".zen-mode-controls .tooltips"
- title="Switch zen theme"
- onClick={this.toggleTheme}></span>
- </li>
- </ul>
- <div className="tooltips"></div>
- </div>
- <CodeEditor
- ref="ace"
- autoFocus={true}
- theme={this.themes[this.state.theme]}
- defaultCode={this.props.defaultCode}
- editorCommands={editorCommands}
- ignorableErrors={this.props.ignorableErrors}
- highlightActiveLine={this.props.highlightActiveLine}
- />
- </div>
- );
- }
-});
-
-
-var Beautify = React.createClass({
- noOfLines: function () {
- return this.props.code.split(/\r\n|\r|\n/).length;
- },
-
- canBeautify: function () {
- return this.noOfLines() === 1;
- },
-
- addTooltip: function () {
- if (this.canBeautify) {
- $('.beautify-tooltip').tooltip({ placement: 'right' });
- }
- },
-
- componentDidMount: function () {
- this.addTooltip();
- },
-
- beautify: function (event) {
- event.preventDefault();
- var beautifiedCode = beautifyHelper(this.props.code);
- this.props.beautifiedCode(beautifiedCode);
- $('.beautify-tooltip').tooltip('hide');
- },
-
- render: function () {
- if (!this.canBeautify()) {
- return null;
- }
-
- return (
- <button
- onClick={this.beautify}
- className="beautify beautify_map btn btn-primary btn-small beautify-tooltip"
- type="button"
- data-toggle="tooltip"
- title="Reformat your minified code to make edits to it."
- >
- beautify this code
- </button>
- );
- }
-});
-
-var PaddedBorderedBox = React.createClass({
- render: function () {
- return (
- <div className="bordered-box">
- <div className="padded-box">
- {this.props.children}
- </div>
- </div>
- );
- }
-});
-
-var Document = React.createClass({
- propTypes: {
- docIdentifier: React.PropTypes.string.isRequired,
- docChecked: React.PropTypes.func.isRequired,
- truncate: React.PropTypes.bool,
- maxRows: React.PropTypes.number
- },
-
- getDefaultProps: function () {
- return {
- truncate: true,
- maxRows: 500
- };
- },
-
- onChange: function (e) {
- e.preventDefault();
- this.props.docChecked(this.props.doc.id, this.props.doc._rev);
- },
-
- getUrlFragment: function () {
- if (!this.props.children) {
- return '';
- }
-
- return (
- <div className="doc-edit-symbol pull-right" title="Edit document">
- {this.props.children}
- </div>
- );
- },
-
- getExtensionIcons: function () {
- var extensions = FauxtonAPI.getExtensions('DocList:icons');
- return _.map(extensions, function (Extension, i) {
- return (<Extension doc={this.props.doc} key={i} />);
- }, this);
- },
-
- getCheckbox: function () {
- if (!this.props.isDeletable) {
- return <div className="checkbox-dummy"></div>;
- }
-
- return (
- <div className="checkbox inline">
- <input
- id={'checkbox-' + this.props.docIdentifier}
- checked={this.props.checked}
- data-checked={this.props.checked}
- type="checkbox"
- onChange={this.onChange}
- className="js-row-select" />
- <label onClick={this.onChange}
- className="label-checkbox-doclist"
- htmlFor={'checkbox-' + this.props.docIdentifier} />
- </div>
- );
- },
-
- onDoubleClick: function (e) {
- this.props.onDoubleClick(this.props.docIdentifier, this.props.doc, e);
- },
-
- getDocContent: function () {
- if (_.isEmpty(this.props.docContent)) {
- return null;
- }
-
- // if need be, truncate the document
- var content = this.props.docContent;
- var isTruncated = false;
- if (this.props.truncate) {
- var result = Helpers.truncateDoc(this.props.docContent, this.props.maxRows);
- isTruncated = result.isTruncated;
- content = result.content;
- }
-
- return (
- <div className="doc-data">
- <pre className="prettyprint">{content}</pre>
- {isTruncated ? <div className="doc-content-truncated">(truncated)</div> : null}
- </div>
- );
- },
-
- render: function () {
- return (
- <div data-id={this.props.docIdentifier} onDoubleClick={this.onDoubleClick} className="doc-row">
- <div className="custom-inputs">
- {this.getCheckbox()}
- </div>
- <div className="doc-item">
- <header>
- <span className="header-keylabel">
- {this.props.keylabel}
- </span>
- <span className="header-doc-id">
- {this.props.header ? '"' + this.props.header + '"' : null}
- </span>
- {this.getUrlFragment()}
- <div className="doc-item-extension-icons pull-right">{this.getExtensionIcons()}</div>
- </header>
- {this.getDocContent()}
- </div>
- <div className="clearfix"></div>
- </div>
- );
- }
-});
-
-var LoadLines = React.createClass({
- render: function () {
- return (
- <div className="loading-lines">
- <div id="line1"> </div>
- <div id="line2"> </div>
- <div id="line3"> </div>
- <div id="line4"> </div>
- </div>
- );
- }
-});
-
-const ConfirmButton = React.createClass({
- propTypes: {
- showIcon: React.PropTypes.bool,
- id: React.PropTypes.string,
- customIcon: React.PropTypes.string,
- style: React.PropTypes.object,
- buttonType: React.PropTypes.string,
- 'data-id': React.PropTypes.string,
- onClick: React.PropTypes.func,
- disabled: React.PropTypes.bool,
- },
-
- getDefaultProps: function () {
- return {
- disabled: false,
- showIcon: true,
- customIcon: 'fonticon-ok-circled',
- buttonType: 'btn-success',
- style: {},
- 'data-id': null,
- onClick: function () { }
- };
- },
-
- getIcon: function () {
- if (!this.props.showIcon) {
- return null;
- }
- return (
- <i className={"icon " + this.props.customIcon} />
- );
- },
-
- render: function () {
- const { onClick, buttonType, id, style, text, disabled } = this.props;
- return (
- <button
- onClick={onClick}
- type="submit"
- disabled={disabled}
- data-id={this.props['data-id']}
- className={'btn save ' + buttonType}
- id={id}
- style={style}
- >
- {this.getIcon()}
- {text}
- </button>
- );
- }
-});
-
-const MenuDropDown = React.createClass({
-
- getDefaultProps: function () {
- return {
- icon: 'fonticon-plus-circled'
- };
- },
-
- createSectionLinks: function (links) {
- if (!links) { return null; }
-
- return links.map((link, key) => {
- return this.createEntry(link, key);
- });
- },
-
- createEntry: function (link, key) {
- return (
- <li key={key}>
- <a className={link.icon ? 'icon ' + link.icon : ''}
- data-bypass={link.external ? 'true' : ''}
- href={link.url}
- onClick={link.onClick}
- target={link.external ? '_blank' : ''}>
- {link.title}
- </a>
- </li>
- );
- },
-
- createSectionTitle: function (title) {
- if (!title) {
- return null;
- }
-
- return (
- <li className="header-label">{title}</li>
- );
- },
-
- createSection: function () {
- return this.props.links.map((linkSection, key) => {
- if (linkSection.title && linkSection.links) {
- return ([
- this.createSectionTitle(linkSection.title),
- this.createSectionLinks(linkSection.links)
- ]);
- }
-
- return this.createEntry(linkSection, 'el' + key);
-
- });
- },
-
- render: function () {
- return (
- <div className="dropdown">
- <a className={"dropdown-toggle icon " + this.props.icon}
- data-toggle="dropdown"
- href="#"
- data-bypass="true"></a>
- <ul className="dropdown-menu arrow" role="menu" aria-labelledby="dLabel">
- {this.createSection()}
- </ul>
- </div>
- );
- }
-});
-
-var TrayContents = React.createClass({
- getChildren: function () {
- var className = "tray show-tray " + this.props.className;
- return (
- <div key={1} id={this.props.id} className={className}>
- {this.props.children}
- </div>);
- },
-
- render: function () {
- return (
- <ReactCSSTransitionGroup transitionName="tray" transitionAppear={true} component="div" transitionAppearTimeout={500}
- transitionEnterTimeout={500} transitionLeaveTimeout={300}>
- {this.getChildren()}
- </ReactCSSTransitionGroup>
- );
- }
-});
-
-
-function connectToStores (Component, stores, getStateFromStores) {
-
- var WrappingElement = React.createClass({
-
- componentDidMount: function () {
- stores.forEach(function (store) {
- store.on('change', this.onChange, this);
- }.bind(this));
- },
-
- componentWillUnmount: function () {
- stores.forEach(function (store) {
- store.off('change', this.onChange);
- }.bind(this));
- },
-
- getInitialState: function () {
- return getStateFromStores(this.props);
- },
-
- onChange: function () {
- if (!this.isMounted()) {
- return;
- }
-
- this.setState(getStateFromStores(this.props));
- },
-
- handleStoresChanged: function () {
- if (this.isMounted()) {
- this.setState(getStateFromStores(this.props));
- }
- },
-
- render: function () {
- return <Component {...this.state} {...this.props} />;
- }
-
- });
-
- return WrappingElement;
-}
-
-var TrayWrapper = React.createClass({
- getDefaultProps: function () {
- return {
- className: ''
- };
- },
-
- renderChildren: function () {
- return React.Children.map(this.props.children, function (child, key) {
-
- const props = {};
- Object.keys(this.props).filter((k) => {
- return this.props.hasOwnProperty(k);
- }).map((k) => {
- return props[k] = this.props[k];
- });
-
- return React.cloneElement(child, props);
- }.bind(this));
- },
-
- render: function () {
- return (
- <div>
- {this.renderChildren()}
- </div>
- );
- }
-});
-
-const APIBar = React.createClass({
- propTypes: {
- buttonVisible: React.PropTypes.bool.isRequired,
- contentVisible: React.PropTypes.bool.isRequired,
- docURL: React.PropTypes.string,
- endpoint: React.PropTypes.string
- },
-
- showCopiedMessage: function () {
- FauxtonAPI.addNotification({
- msg: 'The API URL has been copied to the clipboard.',
- type: 'success',
- clear: true
- });
- },
-
- getDocIcon: function () {
- if (!this.props.docURL) {
- return null;
- }
- return (
- <a
- className="help-link"
- data-bypass="true"
- href={this.props.docURL}
- target="_blank"
- >
- <i className="icon icon-question-sign"></i>
- </a>
- );
- },
-
- getTray: function () {
- if (!this.props.contentVisible) {
- return null;
- }
-
- return (
- <TrayContents className="tray show-tray api-bar-tray">
- <div className="input-prepend input-append">
- <span className="add-on">
- API URL
- {this.getDocIcon()}
- </span>
-
- <FauxtonComponents.ClipboardWithTextField
- onClipBoardClick={this.showCopiedMessage}
- text="Copy URL"
- textToCopy={this.props.endpoint}
- showCopyIcon={false}
- uniqueKey="clipboard-apiurl" />
-
- <div className="add-on">
- <a
- data-bypass="true"
- href={this.props.endpoint}
- target="_blank"
- className="btn"
- >
- View JSON
- </a>
- </div>
- </div>
- </TrayContents>
- );
- },
-
- toggleTrayVisibility: function () {
- Actions.toggleApiBarVisibility(!this.props.contentVisible);
- },
-
- componentDidMount: function () {
- $('body').on('click.APIBar', function (e) {
-
- if (!$('.show-tray.api-bar-tray').length) {
- return;
- }
-
- if ($(e.target).closest('.api-bar-tray,.control-toggle-api-url').length === 0) {
- Actions.toggleApiBarVisibility(false);
- }
- }.bind(this));
- },
-
- componentWillUnmount: function () {
- $('body').off('click.APIBar');
- },
-
- render: function () {
- if (!this.props.buttonVisible || !this.props.endpoint) {
- return null;
- }
-
- return (
- <div>
- <ToggleHeaderButton
- containerClasses="header-control-box control-toggle-api-url"
- title="API URL"
- fonticon="fonticon-link"
- text="API"
- toggleCallback={this.toggleTrayVisibility} />
-
- {this.getTray()}
- </div>
- );
- }
-});
-
-export const ApiBarNoStore = ({docUrl, endpoint}) => {
- return (
- <TrayWrapper docUrl={docUrl} endpoint={endpoint}>
- <APIBar buttonVisible={true} contentVisible={false} />
- </TrayWrapper>
- );
-};
-
-export const ApiBarController = React.createClass({
-
- getWrap: function () {
- return connectToStores(TrayWrapper, [componentStore], function () {
- return {
- buttonVisible: componentStore.getIsAPIBarButtonVisible(),
- contentVisible: componentStore.getIsAPIBarVisible(),
- endpoint: componentStore.getEndpoint(),
- docURL: componentStore.getDocURL()
- };
- });
- },
-
- render: function () {
- const TrayWrapper = this.getWrap();
- return (
- <TrayWrapper>
- <APIBar buttonVisible={true} contentVisible={false} />
- </TrayWrapper>
- );
- }
-});
-
-var DeleteDatabaseModal = React.createClass({
-
- getInitialState: function () {
- return {
- inputValue: '',
- disableSubmit: true
- };
- },
-
- propTypes: {
- showHide: React.PropTypes.func.isRequired,
- modalProps: React.PropTypes.object
- },
-
- close: function (e) {
- if (e) {
- e.preventDefault();
- }
-
- this.setState({
- inputValue: '',
- disableSubmit: true
- });
-
- this.props.showHide({showModal: false});
- },
-
- open: function () {
- this.props.showHide({showModal: true});
- },
-
- getDatabaseName: function () {
- return this.props.modalProps.dbId.trim();
- },
-
- onInputChange: function (e) {
- const val = e.target.value.trim();
-
- this.setState({
- inputValue: val
- });
-
- this.setState({
- disableSubmit: val !== this.getDatabaseName()
- });
- },
-
- onDeleteClick: function (e) {
- e.preventDefault();
-
- Actions.deleteDatabase(this.getDatabaseName());
- },
-
- onInputKeypress: function (e) {
- if (e.keyCode === 13 && this.state.disableSubmit !== true) {
- Actions.deleteDatabase(this.getDatabaseName());
- }
- },
-
- render: function () {
- var isSystemDatabase = this.props.modalProps.isSystemDatabase;
- var showDeleteModal = this.props.modalProps.showDeleteModal;
- var dbId = this.props.modalProps.dbId;
-
- var warning = isSystemDatabase ? (
- <p style={{color: '#d14'}} className="warning">
- <b>You are about to delete a system database, be careful!</b>
- </p>
- ) : null;
-
- return (
- <Modal dialogClassName="delete-db-modal" show={showDeleteModal} onHide={this.close}>
- <Modal.Header closeButton={true}>
- <Modal.Title>Delete Database</Modal.Title>
- </Modal.Header>
- <Modal.Body>
- {warning}
- <p>
- Warning: This action will permanently delete <code>{dbId}</code>.
- To confirm the deletion of the database and all of the
- database's documents, you must enter the database's name.
- </p>
- <input
- type="text"
- className="input-block-level"
- onKeyUp={this.onInputKeypress}
- onChange={this.onInputChange}
- autoFocus={true} />
- </Modal.Body>
- <Modal.Footer>
- <a href="#" onClick={this.close} data-bypass="true" className="cancel-link">Cancel</a>
- <button
- disabled={this.state.disableSubmit}
- onClick={this.onDeleteClick}
- className="btn btn-danger delete">
- <i className="icon fonticon-cancel-circled" /> Delete
- </button>
- </Modal.Footer>
- </Modal>
- );
- }
-});
-
-const TabElement = ({selected, text, onChange, iconClass}) => {
-
- const additionalClass = selected ? 'tab-element-checked' : '';
-
- return (
- <li className={`component-tab-element ${additionalClass}`}>
-
- <label>
- <div className="tab-element-indicator-wrapper">
- <div className="tab-element-indicator"></div>
- </div>
- <div className="tab-element-content">
- <i className={iconClass}></i>
- <input
- type="radio"
- value={text}
- checked={selected}
- onChange={onChange} />
-
- {text}
- </div>
- </label>
- </li>
-
- );
-};
-TabElement.propTypes = {
- selected: React.PropTypes.bool.isRequired,
- text: React.PropTypes.string.isRequired,
- onChange: React.PropTypes.func.isRequired,
- iconClass: React.PropTypes.string,
-};
-
-const TabElementWrapper = ({children}) => {
- return (
- <ul className="component-tab-element-wrapper">
- {children}
- </ul>
- );
-};
-
+import {Badge, BadgeList} from './components/badge';
+import {ToggleHeaderButton} from './components/toggleheaderbutton';
+import {BulkActionComponent} from './components/bulkaction';
+import {StyledSelect} from './components/styledselect';
+import {StringEditModal} from './components/stringeditmodal';
+import {CodeEditorPanel} from './components/codeeditorpanel';
+import {CodeEditor} from './components/codeeditor';
+import {ZenModeOverlay} from './components/zenmodeoverlay';
+import {Beautify} from './components/beautify';
+import {PaddedBorderedBox} from './components/paddedborderbox';
+import {Document} from './components/document';
+import {LoadLines} from './components/loadlines';
+import {ConfirmButton} from './components/confirmbutton';
+import {MenuDropDown} from './components/menudropdown';
+import {TrayContents, TrayWrapper, connectToStores} from './components/tray';
+import {ApiBarController} from './components/apibar';
+import {DeleteDatabaseModal} from './components/deletedatabasemodal';
+import {TabElement, TabElementWrapper} from './components/tabelement';
export default {
- BadgeList: BadgeList,
- Badge: Badge,
- BulkActionComponent: BulkActionComponent,
- ConfirmButton: ConfirmButton,
- ToggleHeaderButton: ToggleHeaderButton,
- StyledSelect: StyledSelect,
- CodeEditorPanel: CodeEditorPanel,
- CodeEditor: CodeEditor,
- StringEditModal: StringEditModal,
- ZenModeOverlay: ZenModeOverlay,
- Beautify: Beautify,
- PaddedBorderedBox: PaddedBorderedBox,
- Document: Document,
- LoadLines: LoadLines,
- MenuDropDown: MenuDropDown,
- TrayContents: TrayContents,
- TrayWrapper: TrayWrapper,
- connectToStores: connectToStores,
- ApiBarController: ApiBarController,
- DeleteDatabaseModal: DeleteDatabaseModal,
- TabElement: TabElement,
- TabElementWrapper: TabElementWrapper
+ BadgeList,
+ Badge,
+ BulkActionComponent,
+ ConfirmButton,
+ ToggleHeaderButton,
+ StyledSelect,
+ CodeEditorPanel,
+ CodeEditor,
+ StringEditModal,
+ ZenModeOverlay,
+ Beautify,
+ PaddedBorderedBox,
+ Document,
+ LoadLines,
+ MenuDropDown,
+ TrayContents,
+ TrayWrapper,
+ connectToStores,
+ ApiBarController,
+ DeleteDatabaseModal,
+ TabElement,
+ TabElementWrapper
};
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/369c3265/app/addons/components/tests/paddedBorderedBoxSpec.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/components/tests/paddedBorderedBoxSpec.react.jsx b/app/addons/components/tests/paddedBorderedBoxSpec.react.jsx
index 3f1b317..5223eac 100644
--- a/app/addons/components/tests/paddedBorderedBoxSpec.react.jsx
+++ b/app/addons/components/tests/paddedBorderedBoxSpec.react.jsx
@@ -37,6 +37,7 @@ describe('PaddedBorderedBox', function () {
</ReactComponents.PaddedBorderedBox>,
container
);
+ console.log(container);
assert.ok($(ReactDOM.findDOMNode(el)).find('.foo-children').length);
});
});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/369c3265/app/addons/fauxton/tests/componentsSpec.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/fauxton/tests/componentsSpec.react.jsx b/app/addons/fauxton/tests/componentsSpec.react.jsx
index 83eb514..46aac7d 100644
--- a/app/addons/fauxton/tests/componentsSpec.react.jsx
+++ b/app/addons/fauxton/tests/componentsSpec.react.jsx
@@ -16,6 +16,7 @@ import React from "react";
import ReactDOM from "react-dom";
import TestUtils from "react-addons-test-utils";
import sinon from "sinon";
+import { mount } from 'enzyme';
var assert = utils.assert;
describe('Tray', function () {
@@ -248,28 +249,19 @@ describe('Pagination', function () {
describe('Clipboard', function () {
- var container;
- beforeEach(function () {
- container = document.createElement('div');
- });
-
- afterEach(function () {
- ReactDOM.unmountComponentAtNode(container);
- });
it('shows a clipboard icon by default', function () {
- var clipboard = TestUtils.renderIntoDocument(<Views.Clipboard text="copy me" />, container);
- assert.equal($(ReactDOM.findDOMNode(clipboard)).find('.icon-paste').length, 1);
+ const clipboard = mount(<Views.Clipboard text="copy me" />);
+ assert.equal(clipboard.find('.icon-paste').length, 1);
});
it('shows text if specified', function () {
- var clipboard = TestUtils.renderIntoDocument(<Views.Clipboard displayType="text" text="copy me" />, container);
- assert.equal($(ReactDOM.findDOMNode(clipboard)).find('.icon-paste').length, 0);
+ const clipboard = mount(<Views.Clipboard text="copy me" displayType="text" />);
+ assert.equal(clipboard.find('.icon-paste').length, 0);
});
it('shows custom text if specified ', function () {
- var clipboard = TestUtils.renderIntoDocument(<Views.Clipboard displayType="text" textDisplay='booyah!' text="copy me" />, container);
- assert.ok(/booyah!/.test($(ReactDOM.findDOMNode(clipboard))[0].outerHTML));
+ var clipboard = mount(<Views.Clipboard displayType="text" textDisplay='booyah!' text="copy me" />);
+ assert.ok(/booyah!/.test(clipboard.html()));
});
-
});