// Update the scroll position of the list view to ensure the selected item is visible. _updateScrollPosition(): void { if (!(this.refs && this.refs['selectionList'])) { return; } const listNode = ReactDOM.findDOMNode(this.refs['selectionList']); const selectedNode = listNode.getElementsByClassName('selected')[0]; // false is passed for @centerIfNeeded parameter, which defaults to true. // Passing false causes the minimum necessary scroll to occur, so the selection sticks to the // top/bottom. if (selectedNode) { selectedNode.scrollIntoViewIfNeeded(false); } }
const dispose = () => { if (popupElement) { ReactDOM.unmountComponentAtNode(popupElement); invariant(popupElement.parentNode != null); popupElement.parentNode.removeChild(popupElement); popupElement = null; } if (paneItemSubscription) { paneItemSubscription.dispose(); paneItemSubscription = null; } clearDisposeTimeout(); };
it('should use the correct, unique radio group name', () => { const props = {optionLabels: ['foo', 'bar'], selectedIndex: 1}; const component = renderIntoDocument( <NuclideRadioGroup {...props} /> ); const radioInputs = scryRenderedDOMComponentsWithTag( component, 'input' ); // Global uid is `1` as this point, since this is the second RadioGroup component to be created. expect(ReactDOM.findDOMNode(radioInputs[0]).getAttribute('name')).toEqual('radiogroup-1'); expect(ReactDOM.findDOMNode(radioInputs[1]).getAttribute('name')).toEqual('radiogroup-1'); const component2 = renderIntoDocument( <NuclideRadioGroup {...props} /> ); const radioInputs2 = scryRenderedDOMComponentsWithTag( component2, 'input' ); expect(ReactDOM.findDOMNode(radioInputs2[0]).getAttribute('name')).toEqual('radiogroup-2'); expect(ReactDOM.findDOMNode(radioInputs2[1]).getAttribute('name')).toEqual('radiogroup-2'); });
/** * Update the attributes on the current element. Custom attributes won't be added by React because * "webview" isn't a valid custom element name (custom elements need a dash), so we set the * attributes ourselves. But not "className" or "style" because React has special rules for those. * *sigh* */ updateAttributes(prevProps: Object): void { const element = ReactDOM.findDOMNode(this); const specialProps = ['className', 'style', 'onDidFinishLoad']; const normalProps = Object.keys(this.props).filter(prop => specialProps.indexOf(prop) === -1); normalProps.forEach(prop => { const value = this.props[prop]; const prevValue = prevProps[prop]; const valueChanged = value !== prevValue; if (valueChanged) { element[prop] = value; } }); }
_renderActiveModal(): void { if (this.state.activeModalComponent == null) { return; } let panel = this._activeModalPanel; if (panel == null) { const item = document.createElement('div'); panel = this._activeModalPanel = atom.workspace.addModalPanel({item}); } ReactDOM.render(this.state.activeModalComponent, panel.getItem()); }
renderState(state: RefactorState) { if (state.type === 'open') { if (this._panel == null) { const element = document.createElement('div'); this._panel = atom.workspace.addModalPanel({item: element}); } ReactDOM.render( <MainRefactorComponent appState={state} store={this._store} />, this._panel.getItem(), ); } else { if (this._panel != null) { const panel = this._panel; ReactDOM.unmountComponentAtNode(panel.getItem()); panel.destroy(); this._panel = null; } } }
_render(): QuickSelectionComponent { const component = ReactDOM.render( <QuickSelectionComponent activeProvider={this._currentProvider} onProviderChange={this.handleActiveProviderChange.bind(this)} maxScrollableAreaHeight={this._maxScrollableAreaHeight} onBlur={this.closeSearchPanel.bind(this)} />, this._reactDiv ); invariant(component instanceof QuickSelectionComponent); return component; }
_renderEditors(): void { const {filePath, oldEditorState: oldState, newEditorState: newState} = this.state; this._oldEditorComponent = ReactDOM.render( <DiffViewEditorPane headerTitle={oldState.revisionTitle} textBuffer={this._readonlyBuffer} filePath={filePath} offsets={oldState.offsets} highlightedLines={oldState.highlightedLines} savedContents={oldState.text} initialTextContent={oldState.text} inlineElements={oldState.inlineElements} handleNewOffsets={this._handleNewOffsets} readOnly={true} onChange={EMPTY_FUNCTION} onDidUpdateTextEditorElement={EMPTY_FUNCTION} />, this._getPaneElement(this._oldEditorPane), ); const textBuffer = bufferForUri(filePath); this._newEditorComponent = ReactDOM.render( <DiffViewEditorPane headerTitle={newState.revisionTitle} textBuffer={textBuffer} filePath={filePath} offsets={newState.offsets} highlightedLines={newState.highlightedLines} initialTextContent={newState.text} savedContents={newState.savedContents} inlineElements={newState.inlineElements} handleNewOffsets={this._handleNewOffsets} onDidUpdateTextEditorElement={this._onDidUpdateTextEditorElement} readOnly={false} onChange={this._onChangeNewTextEditor} />, this._getPaneElement(this._newEditorPane), ); }
beforeEach(() => { spyOn(Date, 'now').andCallFake(() => window.now); componentRoot = document.createElement('div'); document.body.appendChild(componentRoot); const testProvider = new TestQuickSelectionProvider({}); component = ReactDOM.render( <QuickSelectionComponent provider={testProvider} />, componentRoot ); });
.forEach(pane => { const items = pane.getItems(); const activeItem = pane.getActiveItem(); // Iterate in reverse so that we can't get tripped up by the items we're adding. for (let index = items.length - 1; index >= 0; index--) { const item = items[index]; // If the item is a placeholder, try to replace it. If we were successful, then we know // the item is up-to-date, so there's no need to update it and we can move on to the // next item. if (this.replacePlaceholder(item, pane, index) != null) { continue; } const GadgetComponent = state.get('components').get(item); // If there's no component for this item, it isn't a gadget. if (GadgetComponent == null) { continue; } // Update the props for the item. const oldProps = state.get('props').get(item); const newProps = { ...oldProps, active: item === activeItem, }; // Don't re-render if the props haven't changed. if (shallowEqual(oldProps, newProps)) { continue; } // Re-render the item with the new props. ReactDOM.render( <GadgetComponent {...newProps} />, item.element, ); // $FlowIssue(t10268095) this._observer.onNext({ type: ActionTypes.UPDATE_PANE_ITEM, payload: { item, props: newProps, }, }); } });
_onClick(event: SyntheticMouseEvent) { const deep = event.altKey; if ( ReactDOM.findDOMNode(this.refs['arrowContainer']).contains(event.target) && event.clientX < ReactDOM.findDOMNode( this.refs['pathContainer']).getBoundingClientRect().left ) { this._toggleNodeExpanded(deep); return; } const modifySelection = event.ctrlKey || event.metaKey; if (modifySelection) { getActions().toggleSelectNode(this.props.rootKey, this.props.nodeKey); } else { if (!this.props.isSelected) { getActions().selectSingleNode(this.props.rootKey, this.props.nodeKey); } if (this.props.isSelected || this.props.usePreviewTabs) { this._toggleNodeExpanded(deep); } } }
dispose(): void { if (this._mouseUpTimeout != null) { clearTimeout(this._mouseUpTimeout); } if (this._marker != null) { this._marker.destroy(); } if (this._mouseSubscription != null) { this._mouseSubscription.unsubscribe(); } ReactDOM.unmountComponentAtNode(this._hostElement); this._hostElement.remove(); this._subscriptions.dispose(); }
waitsForPromise(async () => { invariant(renderComponent); const component = renderComponent(props); await component.setRoots([nodes['G']]); expect(component.getSelectedNodes()).toEqual([]); const nodeComponents = getNodeComponents(component); TestUtils.Simulate.click(ReactDOM.findDOMNode(nodeComponents['G'].refs['arrow'])); expect(component.getSelectedNodes()).toEqual([]); invariant(onConfirmSelection); expect(onConfirmSelection.callCount).toBe(0); });
_measureHeights(): void { const measuredComponent = this.refs['measured']; if (measuredComponent == null) { return; } this._initialHeightMeasured = true; const node = ReactDOM.findDOMNode(measuredComponent); const elementHeight = node.clientHeight; if (elementHeight !== this.state.elementHeight && elementHeight > 0) { this.setState({elementHeight}); } }
function updateToolbarCount(diffViewButton: HTMLElement, count: number): void { if (!changeCountElement) { changeCountElement = document.createElement('span'); changeCountElement.className = 'diff-view-count'; diffViewButton.appendChild(changeCountElement); } if (count > 0) { diffViewButton.classList.add('positive-count'); } else { diffViewButton.classList.remove('positive-count'); } const DiffCountComponent = require('./DiffCountComponent'); ReactDOM.render(<DiffCountComponent count={count} />, changeCountElement); }
it('does not expand on click when node is selected', () => { const nodeComponent = renderEntryComponentIntoDocument( FileTreeEntryComponent, { rootUri: '/a/', uri: '/a/b', isSelected: true, isContainer: false, } ); const domNode = ReactDOM.findDOMNode(nodeComponent); TestUtils.Simulate.click(domNode); expect(actions.expandNode).not.toHaveBeenCalled(); });
_renderPanel(didRender?: () => mixed) { // Initialize and render the contents of the panel only if the hosting container is visible by // the user's choice. if (!this._state.panelVisible) { return; } let root = this._root; if (!root) { root = document.createElement('div'); this._root = root; } let progressValue; if (this._testSuiteModel && this._executionState === TestRunnerPanel.ExecutionState.RUNNING) { progressValue = this._testSuiteModel.progressPercent(); } else { // If there is no running test suite, fill the progress bar because there is no progress to // track. progressValue = 100; } this._testRunnerPanel = ReactDOM.render( <TestRunnerPanel attachDebuggerBeforeRunning={this._attachDebuggerBeforeRunning} buffer={this._buffer} executionState={this._executionState} onClickClear={this.clearOutput} onClickClose={this.hidePanel} onClickRun={this._handleClickRun} onClickStop={this.stopTests} onDebuggerCheckboxChanged={this._onDebuggerCheckboxChanged} path={this._path} progressValue={progressValue} runDuration={this._run && this._run.getDuration()} // `TestRunnerPanel` expects an Array so it can render the test runners in a dropdown and // maintain a selected index. `Set` maintains items in insertion order, so the ordering is // determinate on each render. testRunners={Array.from(this._testRunners)} testSuiteModel={this._testSuiteModel} />, root, didRender ); if (!this._panel) { this._panel = atom.workspace.addBottomPanel({item: root, visible: this._state.panelVisible}); } }
componentDidMount(): void { const {diffModel} = this.props; this._subscriptions.add(diffModel.onActiveFileUpdates(activeFileState => { this._updateLineDiffState(activeFileState); // The diff tree needs to update the active diffed file. // TODO(most): merge ActiveFileState into DiffModel's State. this._renderTree(); })); this._subscriptions.add(diffModel.onDidUpdateState(this._onModelStateChange)); this._subscriptions.add(atom.workspace.onDidChangeActivePaneItem(activeItem => { if (activeItem != null && (activeItem: any).tagName === 'NUCLIDE-DIFF-VIEW') { // Re-render on activation. this._updateLineDiffState(diffModel.getActiveFileState()); } })); this._paneContainer = createPaneContainer(); // The changed files status tree takes 1/5 of the width and lives on the right most, // while being vertically splt with the revision timeline stack pane. const topPane = this._newEditorPane = this._paneContainer.getActivePane(); this._bottomRightPane = topPane.splitDown({ flexScale: 0.3, }); this._treePane = this._bottomRightPane.splitLeft({ flexScale: 0.35, }); this._navigationPane = topPane.splitRight({ flexScale: 0.045, }); this._oldEditorPane = topPane.splitLeft({ flexScale: 1, }); this._renderDiffView(); this._subscriptions.add( this._destroyPaneDisposable(this._oldEditorPane), this._destroyPaneDisposable(this._newEditorPane), this._destroyPaneDisposable(this._navigationPane), this._destroyPaneDisposable(this._treePane), this._destroyPaneDisposable(this._bottomRightPane), ); ReactDOM.findDOMNode(this.refs.paneContainer).appendChild( atom.views.getView(this._paneContainer), ); this._updateLineDiffState(diffModel.getActiveFileState()); }
_renderStatusBar(connectionState: number, fileUri?: string): void { if (!this._statusBarDiv) { return; } const component = ReactDOM.render( <StatusBarTile connectionState={connectionState} fileUri={fileUri} />, this._statusBarDiv, ); invariant(component instanceof StatusBarTile); this._statusBarTile = component; }
function renderPanel(renderState: State, onDidRender?: () => mixed): void { const activeViewInstance = getActiveViewInstance(renderState); panelComponent = ReactDOM.render( <PanelComponent dock="left" hidden={renderState.hidden} initialLength={renderState.initialLength}> {activeViewInstance == null ? <div /> : React.createElement(activeViewInstance.view.getComponent())} </PanelComponent>, item, onDidRender ); }
waitsForPromise(async () => { invariant(renderComponent); const component = renderComponent(props); await component.setRoots([nodes.G]); await component.expandNodeKey(nodes.H.getKey()); expect(component.getSelectedNodes()).toEqual([]); const nodeComponents = getNodeComponents(component); TestUtils.Simulate.click(ReactDOM.findDOMNode(nodeComponents.J)); expect(component.getSelectedNodes()).toEqual([nodes.J]); invariant(onConfirmSelection); expect(onConfirmSelection.callCount).toBe(1); });
dispose(): void { if (this._item) { ReactDOM.unmountComponentAtNode(this._item); this._item = null; } if (this._tile) { this._tile.destroy(); this._tile = null; } if (this._tooltip) { this._tooltip.dispose(); this._tooltip = null; } this._isMouseOver = false; }
it('opens a file if a selected node is clicked', () => { const nodeComponent = renderEntryComponentIntoDocument( FileTreeEntryComponent, { rootUri: '/a/', uri: '/a/b', isSelected: true, isContainer: false, usePreviewTabs: true, }, ); const domNode = ReactDOM.findDOMNode(nodeComponent); TestUtils.Simulate.click(domNode); expect(actions.confirmNode).toHaveBeenCalled(); });
waitsForPromise(async () => { invariant(renderComponent); const component = renderComponent(props); await component.setRoots([nodes['G']]); expect(component.getSelectedNodes()).toEqual([]); const nodeComponents = getNodeComponents(component); TestUtils.Simulate.mouseDown(ReactDOM.findDOMNode(nodeComponents['G']), {button: 2}); expect(component.getSelectedNodes()).toEqual([nodeComponents['G'].props.node]); TestUtils.Simulate.mouseDown( ReactDOM.findDOMNode(nodeComponents['H']), {button: 0, ctrlKey: true} ); expect(component.getSelectedNodes()).toEqual([nodeComponents['H'].props.node]); TestUtils.Simulate.mouseDown(ReactDOM.findDOMNode(nodeComponents['I']), {button: 0}); expect(component.getSelectedNodes()).toEqual([nodeComponents['H'].props.node]); invariant(onConfirmSelection); expect(onConfirmSelection.callCount).toBe(0); });
constructor(rawState: ?Object) { rawState = rawState || {}; this._disposables = new CompositeDisposable( atom.commands.add( 'body', 'nuclide-buck-toolbar:toggle', () => { this._actions.togglePanelVisibility(); }, ), ); const initialState = { buildTarget: rawState.buildTarget || null, isPanelVisible: rawState.isPanelVisible || false, isReactNativeServerMode: rawState.isReactNativeServerMode || false, }; const dispatcher = new Dispatcher(); this._store = new BuckToolbarStore(dispatcher, initialState); this._disposables.add(this._store); this._actions = new BuckToolbarActions(dispatcher); const container = document.createElement('div'); ReactDOM.render( <BuckToolbar store={this._store} actions={this._actions} />, container, ); const panel = atom.workspace.addTopPanel({ item: container, // Increase priority (default is 100) to ensure this toolbar comes after the 'tool-bar' // package's toolbar. Hierarchically the controlling toolbar should be above, and practically // this ensures the popover in this build toolbar stacks on top of other UI. priority: 200, }); this._disposables.add( new Disposable(() => { ReactDOM.unmountComponentAtNode(container); panel.destroy(); }), ); const target = 'atom-workspace'; this._disposables.add( atom.commands.add(target, 'nuclide-buck-toolbar:build', () => this._actions.build()), atom.commands.add(target, 'nuclide-buck-toolbar:debug', () => this._actions.debug()), atom.commands.add(target, 'nuclide-buck-toolbar:run', () => this._actions.run()), atom.commands.add(target, 'nuclide-buck-toolbar:test', () => this._actions.test()), ); }
waitsForPromise(() => new Promise((resolve: (component: any) => any, reject) => { component.onItemsChanged(newItems => { resolve(component); }); component = ReactDOM.render( <QuickSelectionComponent provider={new TestQuickSelectionProvider(items)} />, componentRoot ); window.advanceClock(250); component.clear(); }).then(callback));
componentDidMount(): void { const node = ReactDOM.findDOMNode(this); node.focus(); this._disposables.add(atom.commands.add( node, { 'core:move-up': () => this._moveSelectionIndex(-1), 'core:move-down': () => this._moveSelectionIndex(1), 'core:confirm': () => { const def = this.state.applicableDefinitions[this.state.selectionIndex]; this._toggleWorkingSet(def.name, def.active); }, 'core:cancel': this.props.onClose, } )); }
componentDidMount(): void { const node = ReactDOM.findDOMNode(this); node.focus(); this._disposables.add(atom.commands.add( node, { 'file-tree:workset-select-up': () => this._moveSelectionIndex(-1), 'file-tree:workset-select-down': () => this._moveSelectionIndex(1), 'file-tree:workset-select-toggle': () => { const def = this.state.definitions[this.state.selectionIndex]; this._toggleWorkingSet(def.name, def.active); }, 'file-tree:workset-select-close': this.props.onClose, } )); }
it('expands on click when node is selected', () => { const nodeComponent = renderEntryComponentIntoDocument( FileTreeEntryComponent, { rootUri: '/a/', uri: '/a/b/', isSelected: true, isContainer: true, } ); // The onClick is listened not by the <li> element, but by its first child. const domNode = ReactDOM.findDOMNode(nodeComponent).children[0]; TestUtils.Simulate.click(domNode); expect(actions.expandNode).toHaveBeenCalled(); });
componentDidMount() { const element = ReactDOM.findDOMNode(this); // Add event listeners. This has the drawbacks of 1) adding an event listener even when we don't // have a callback for it and 2) needing to add explicit support for each event type we want to // support. However, those costs aren't great enough to justify a new abstraction for managing // it at this time. element.addEventListener('did-finish-load', this._handleDidFinishLoad); this._disposables.add( new Disposable( () => element.removeEventListener('did-finish-load', this._handleDidFinishLoad), ), ); this.updateAttributes({}); }