mirror of
https://github.com/Kong/insomnia
synced 2024-11-08 06:39:48 +00:00
Add pull dropdown to homepage (#3074)
This commit is contained in:
parent
cf24515d48
commit
8e7c85c5ff
@ -86,7 +86,7 @@ export function update(
|
||||
|
||||
export async function updateByParentId(
|
||||
workspaceId: string,
|
||||
patch: Object = {},
|
||||
patch: $Shape<WorkspaceMeta> = {},
|
||||
): Promise<WorkspaceMeta> {
|
||||
const meta = await getByParentId(workspaceId);
|
||||
return db.docUpdate(meta, patch);
|
||||
|
@ -671,6 +671,7 @@ describe('VCS', () => {
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('describeChanges()', () => {
|
||||
it('works with same object structure', async () => {
|
||||
const a = { foo: 'bar', nested: { baz: 10 } };
|
||||
@ -690,4 +691,82 @@ describe('VCS', () => {
|
||||
expect(describeChanges(a, b)).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getHistory()', () => {
|
||||
let v;
|
||||
beforeEach(async () => {
|
||||
v = await vcs('master');
|
||||
|
||||
const status1 = await v.status(
|
||||
[{ key: 'foo', name: 'Foo', document: newDoc('foobar1') }],
|
||||
{},
|
||||
);
|
||||
const stage1 = await v.stage(status1.stage, [status1.unstaged.foo]);
|
||||
await v.takeSnapshot(stage1, 'Add foo');
|
||||
|
||||
const status2 = await v.status(
|
||||
[{ key: 'bar', name: 'Bar', document: newDoc('foobar2') }],
|
||||
{},
|
||||
);
|
||||
const stage2 = await v.stage(status2.stage, [status2.unstaged.bar]);
|
||||
await v.takeSnapshot(stage2, 'Add bar');
|
||||
});
|
||||
|
||||
it('returns all history', async () => {
|
||||
// get all history
|
||||
expect(await v.getHistory()).toStrictEqual([
|
||||
{
|
||||
author: '',
|
||||
created: expect.any(Date),
|
||||
description: '',
|
||||
id: 'f271aed3e12317215491ca1545770bd8289948e1',
|
||||
name: 'Add foo',
|
||||
parent: '0000000000000000000000000000000000000000',
|
||||
state: [
|
||||
{
|
||||
blob: 'f3827e9fdf461634c4ce528b88ced46fecf6509c',
|
||||
key: 'foo',
|
||||
name: 'Foo',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
author: '',
|
||||
created: expect.any(Date),
|
||||
description: '',
|
||||
id: '3acf54915cd8d34a9cd9ea4ac3a988105b483869',
|
||||
name: 'Add bar',
|
||||
parent: 'f271aed3e12317215491ca1545770bd8289948e1',
|
||||
state: [
|
||||
{
|
||||
blob: 'f3827e9fdf461634c4ce528b88ced46fecf6509c',
|
||||
key: 'foo',
|
||||
name: 'Foo',
|
||||
},
|
||||
{
|
||||
blob: '2d1f409ab33b0b997d6c239a7754d24875570c2d',
|
||||
key: 'bar',
|
||||
name: 'Bar',
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('returns recent history', async () => {
|
||||
const [s1, s2, ...others] = await v.getHistory();
|
||||
|
||||
// There should only be two items
|
||||
expect(others).toHaveLength(0);
|
||||
|
||||
// Get the latest item
|
||||
expect(await v.getHistory(1)).toStrictEqual([s2]);
|
||||
|
||||
// Get the last 2 items
|
||||
expect(await v.getHistory(2)).toStrictEqual([s1, s2]);
|
||||
|
||||
// Get the last 3 items (only 2 exist)
|
||||
expect(await v.getHistory(3)).toStrictEqual([s1, s2]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -379,10 +379,14 @@ export default class VCS {
|
||||
return branch.snapshots.length;
|
||||
}
|
||||
|
||||
async getHistory(): Promise<Array<Snapshot>> {
|
||||
async getHistory(count: number = 0): Promise<Array<Snapshot>> {
|
||||
const branch = await this._getCurrentBranch();
|
||||
const snapshots = [];
|
||||
for (const id of branch.snapshots) {
|
||||
|
||||
const total = branch.snapshots.length;
|
||||
const slice = count <= 0 || count > total ? 0 : total - count;
|
||||
|
||||
for (const id of branch.snapshots.slice(slice)) {
|
||||
const snapshot = await this._getSnapshot(id);
|
||||
if (snapshot === null) {
|
||||
throw new Error(`Failed to get snapshot id=${id}`);
|
||||
|
@ -0,0 +1,189 @@
|
||||
// @flow
|
||||
|
||||
import * as React from 'react';
|
||||
import { autoBindMethodsForReact } from 'class-autobind-decorator';
|
||||
import { AUTOBIND_CFG } from '../../../common/constants';
|
||||
import * as session from '../../../account/session';
|
||||
import VCS from '../../../sync/vcs';
|
||||
import type { Project } from '../../../sync/types';
|
||||
import { Dropdown, DropdownDivider, DropdownItem, Button } from 'insomnia-components';
|
||||
import type { Workspace } from '../../../models/workspace';
|
||||
import HelpTooltip from '../help-tooltip';
|
||||
import * as models from '../../../models';
|
||||
import * as db from '../../../common/database';
|
||||
import { showAlert } from '../modals';
|
||||
|
||||
type Props = {
|
||||
className?: string,
|
||||
vcs?: VCS,
|
||||
workspaces: Array<Workspace>,
|
||||
};
|
||||
|
||||
type State = {
|
||||
loading: boolean,
|
||||
localProjects: Array<Project>,
|
||||
pullingProjects: { [string]: boolean },
|
||||
remoteProjects: Array<Project>,
|
||||
};
|
||||
|
||||
@autoBindMethodsForReact(AUTOBIND_CFG)
|
||||
class RemoteWorkspacesDropdown extends React.Component<Props, State> {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
loading: false,
|
||||
localProjects: [],
|
||||
pullingProjects: {},
|
||||
remoteProjects: [],
|
||||
};
|
||||
}
|
||||
|
||||
async _refreshRemoteWorkspaces() {
|
||||
const { vcs } = this.props;
|
||||
if (!vcs) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!session.isLoggedIn()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({ loading: true });
|
||||
|
||||
const remoteProjects = await vcs.remoteProjects();
|
||||
const localProjects = await vcs.localProjects();
|
||||
this.setState({ remoteProjects, localProjects, loading: false });
|
||||
}
|
||||
|
||||
async _handlePullRemoteWorkspace(project: Project) {
|
||||
const { vcs } = this.props;
|
||||
if (!vcs) {
|
||||
throw new Error('VCS is not defined');
|
||||
}
|
||||
|
||||
this.setState(state => ({
|
||||
pullingProjects: { ...state.pullingProjects, [project.id]: true },
|
||||
}));
|
||||
|
||||
try {
|
||||
// Clone old VCS so we don't mess anything up while working on other projects
|
||||
const newVCS = vcs.newInstance();
|
||||
|
||||
// Remove all projects for workspace first
|
||||
await newVCS.removeProjectsForRoot(project.rootDocumentId);
|
||||
|
||||
// Set project, checkout master, and pull
|
||||
const defaultBranch = 'master';
|
||||
|
||||
await newVCS.setProject(project);
|
||||
await newVCS.checkout([], defaultBranch);
|
||||
|
||||
const remoteBranches = await newVCS.getRemoteBranches();
|
||||
const defaultBranchMissing = !remoteBranches.includes(defaultBranch);
|
||||
|
||||
// The default branch does not exist, so we create it and the workspace locally
|
||||
if (defaultBranchMissing) {
|
||||
const workspace: Workspace = await models.initModel(models.workspace.type, {
|
||||
_id: project.rootDocumentId,
|
||||
name: project.name,
|
||||
});
|
||||
|
||||
await db.upsert(workspace);
|
||||
} else {
|
||||
await newVCS.pull([]); // There won't be any existing docs since it's a new pull
|
||||
|
||||
const flushId = await db.bufferChanges();
|
||||
for (const doc of await newVCS.allDocuments()) {
|
||||
await db.upsert(doc);
|
||||
}
|
||||
await db.flushChanges(flushId);
|
||||
}
|
||||
|
||||
await this._refreshRemoteWorkspaces();
|
||||
} catch (err) {
|
||||
this._dropdown && this._dropdown.hide();
|
||||
showAlert({
|
||||
title: 'Pull Error',
|
||||
message: `Failed to pull workspace. ${err.message}`,
|
||||
});
|
||||
}
|
||||
|
||||
this.setState(state => ({
|
||||
pullingProjects: { ...state.pullingProjects, [project.id]: false },
|
||||
}));
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Props) {
|
||||
// Reload workspaces if we just got a new VCS instance
|
||||
if (this.props.vcs && !prevProps.vcs) {
|
||||
this._refreshRemoteWorkspaces();
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this._refreshRemoteWorkspaces();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { className, workspaces } = this.props;
|
||||
|
||||
const { loading, remoteProjects, localProjects, pullingProjects } = this.state;
|
||||
|
||||
if (!session.isLoggedIn()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const missingRemoteProjects = remoteProjects.filter(({ id, rootDocumentId }) => {
|
||||
const localProjectExists = localProjects.find(p => p.id === id);
|
||||
const workspaceExists = workspaces.find(w => w._id === rootDocumentId);
|
||||
|
||||
// Mark as missing if:
|
||||
// - the project doesn't yet exists locally
|
||||
// - the project exists locally but somehow the workspace doesn't anymore
|
||||
return !(workspaceExists && localProjectExists);
|
||||
});
|
||||
|
||||
const button = (
|
||||
<Button variant="contained" bg="surprise" className={className}>
|
||||
Pull
|
||||
<i className="fa fa-caret-down pad-left-sm" />
|
||||
</Button>
|
||||
);
|
||||
|
||||
return (
|
||||
<Dropdown onOpen={this._refreshRemoteWorkspaces} renderButton={button}>
|
||||
<DropdownDivider>
|
||||
Remote Workspaces{' '}
|
||||
<HelpTooltip>
|
||||
These workspaces have been shared with you via Insomnia Sync and do not yet exist on
|
||||
your machine.
|
||||
</HelpTooltip>{' '}
|
||||
{loading && <i className="fa fa-spin fa-refresh" />}
|
||||
</DropdownDivider>
|
||||
{missingRemoteProjects.length === 0 && (
|
||||
<DropdownItem disabled>Nothing to pull</DropdownItem>
|
||||
)}
|
||||
{missingRemoteProjects.map(p => (
|
||||
<DropdownItem
|
||||
key={p.id}
|
||||
stayOpenAfterClick
|
||||
onClick={() => this._handlePullRemoteWorkspace(p)}
|
||||
icon={
|
||||
pullingProjects[p.id] ? (
|
||||
<i className="fa fa-refresh fa-spin" />
|
||||
) : (
|
||||
<i className="fa fa-cloud-download" />
|
||||
)
|
||||
}>
|
||||
<span>
|
||||
Pull <strong>{p.name}</strong>
|
||||
</span>
|
||||
</DropdownItem>
|
||||
))}
|
||||
</Dropdown>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default RemoteWorkspacesDropdown;
|
@ -11,11 +11,10 @@ import DropdownHint from '../base/dropdown/dropdown-hint';
|
||||
import SettingsModal, { TAB_INDEX_EXPORT } from '../modals/settings-modal';
|
||||
import * as models from '../../../models';
|
||||
|
||||
import { showAlert, showError, showModal, showPrompt } from '../modals';
|
||||
import { showError, showModal, showPrompt } from '../modals';
|
||||
import Link from '../base/link';
|
||||
import WorkspaceSettingsModal from '../modals/workspace-settings-modal';
|
||||
import LoginModal from '../modals/login-modal';
|
||||
import Tooltip from '../tooltip';
|
||||
import KeydownBinder from '../keydown-binder';
|
||||
import type { HotKeyRegistry } from '../../../common/hotkeys';
|
||||
import { hotKeyRefs } from '../../../common/hotkeys';
|
||||
@ -24,8 +23,6 @@ import type { Workspace } from '../../../models/workspace';
|
||||
import SyncShareModal from '../modals/sync-share-modal';
|
||||
import * as db from '../../../common/database';
|
||||
import VCS from '../../../sync/vcs';
|
||||
import HelpTooltip from '../help-tooltip';
|
||||
import type { Project } from '../../../sync/types';
|
||||
import PromptButton from '../base/prompt-button';
|
||||
import * as session from '../../../account/session';
|
||||
import type { WorkspaceAction } from '../../../plugins';
|
||||
@ -52,9 +49,6 @@ type Props = {
|
||||
type State = {
|
||||
actionPlugins: Array<WorkspaceAction>,
|
||||
loadingActions: { [string]: boolean },
|
||||
localProjects: Array<Project>,
|
||||
pullingProjects: { [string]: boolean },
|
||||
remoteProjects: Array<Project>,
|
||||
};
|
||||
|
||||
@autoBindMethodsForReact(AUTOBIND_CFG)
|
||||
@ -64,9 +58,6 @@ class WorkspaceDropdown extends React.PureComponent<Props, State> {
|
||||
state = {
|
||||
actionPlugins: [],
|
||||
loadingActions: {},
|
||||
localProjects: [],
|
||||
pullingProjects: {},
|
||||
remoteProjects: [],
|
||||
};
|
||||
|
||||
async _handlePluginClick(p: WorkspaceAction) {
|
||||
@ -111,86 +102,11 @@ class WorkspaceDropdown extends React.PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
async _handleDropdownOpen() {
|
||||
this._refreshRemoteWorkspaces();
|
||||
|
||||
// Load action plugins
|
||||
const plugins = await getWorkspaceActions();
|
||||
this.setState({ actionPlugins: plugins });
|
||||
}
|
||||
|
||||
async _refreshRemoteWorkspaces() {
|
||||
const { vcs } = this.props;
|
||||
if (!vcs) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!session.isLoggedIn()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const remoteProjects = await vcs.remoteProjects();
|
||||
const localProjects = await vcs.localProjects();
|
||||
this.setState({ remoteProjects, localProjects });
|
||||
}
|
||||
|
||||
async _handlePullRemoteWorkspace(project: Project) {
|
||||
const { vcs } = this.props;
|
||||
if (!vcs) {
|
||||
throw new Error('VCS is not defined');
|
||||
}
|
||||
|
||||
this.setState(state => ({
|
||||
pullingProjects: { ...state.pullingProjects, [project.id]: true },
|
||||
}));
|
||||
|
||||
try {
|
||||
// Clone old VCS so we don't mess anything up while working on other projects
|
||||
const newVCS = vcs.newInstance();
|
||||
|
||||
// Remove all projects for workspace first
|
||||
await newVCS.removeProjectsForRoot(project.rootDocumentId);
|
||||
|
||||
// Set project, checkout master, and pull
|
||||
const defaultBranch = 'master';
|
||||
|
||||
await newVCS.setProject(project);
|
||||
await newVCS.checkout([], defaultBranch);
|
||||
|
||||
const remoteBranches = await newVCS.getRemoteBranches();
|
||||
const defaultBranchMissing = !remoteBranches.includes(defaultBranch);
|
||||
|
||||
// The default branch does not exist, so we create it and the workspace locally
|
||||
if (defaultBranchMissing) {
|
||||
const workspace: Workspace = await models.initModel(models.workspace.type, {
|
||||
_id: project.rootDocumentId,
|
||||
name: project.name,
|
||||
});
|
||||
|
||||
await db.upsert(workspace);
|
||||
} else {
|
||||
await newVCS.pull([]); // There won't be any existing docs since it's a new pull
|
||||
|
||||
const flushId = await db.bufferChanges();
|
||||
for (const doc of await newVCS.allDocuments()) {
|
||||
await db.upsert(doc);
|
||||
}
|
||||
await db.flushChanges(flushId);
|
||||
}
|
||||
|
||||
await this._refreshRemoteWorkspaces();
|
||||
} catch (err) {
|
||||
this._dropdown && this._dropdown.hide();
|
||||
showAlert({
|
||||
title: 'Pull Error',
|
||||
message: `Failed to pull workspace. ${err.message}`,
|
||||
});
|
||||
}
|
||||
|
||||
this.setState(state => ({
|
||||
pullingProjects: { ...state.pullingProjects, [project.id]: false },
|
||||
}));
|
||||
}
|
||||
|
||||
_setDropdownRef(n: ?Dropdown) {
|
||||
this._dropdown = n;
|
||||
}
|
||||
@ -234,17 +150,6 @@ class WorkspaceDropdown extends React.PureComponent<Props, State> {
|
||||
});
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Props) {
|
||||
// Reload workspaces if we just got a new VCS instance
|
||||
if (this.props.vcs && !prevProps.vcs) {
|
||||
this._refreshRemoteWorkspaces();
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this._refreshRemoteWorkspaces();
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
displayName,
|
||||
@ -258,21 +163,6 @@ class WorkspaceDropdown extends React.PureComponent<Props, State> {
|
||||
...other
|
||||
} = this.props;
|
||||
|
||||
const { remoteProjects, localProjects, pullingProjects } = this.state;
|
||||
|
||||
const missingRemoteProjects = remoteProjects.filter(({ id, rootDocumentId }) => {
|
||||
const localProjectExists = localProjects.find(p => p.id === id);
|
||||
const workspaceExists = workspaces.find(w => w._id === rootDocumentId);
|
||||
|
||||
// Mark as missing if:
|
||||
// - the project doesn't yet exists locally
|
||||
// - the project exists locally but somehow the workspace doesn't anymore
|
||||
return !(workspaceExists && localProjectExists);
|
||||
});
|
||||
|
||||
const nonActiveWorkspaces = workspaces
|
||||
.filter(w => w._id !== activeWorkspace._id)
|
||||
.sort((w1, w2) => w1.name.localeCompare(w2.name));
|
||||
const classes = classnames(className, 'wide', 'workspace-dropdown');
|
||||
|
||||
const { actionPlugins, loadingActions } = this.state;
|
||||
@ -303,50 +193,10 @@ class WorkspaceDropdown extends React.PureComponent<Props, State> {
|
||||
<i className="fa fa-globe" /> Share <strong>{activeWorkspace.name}</strong>
|
||||
</DropdownItem>
|
||||
|
||||
<DropdownDivider>Switch Workspace</DropdownDivider>
|
||||
|
||||
{nonActiveWorkspaces.map(w => {
|
||||
const isUnseen = !!unseenWorkspaces.find(v => v._id === w._id);
|
||||
return (
|
||||
<DropdownItem key={w._id} onClick={handleSetActiveWorkspace} value={w._id}>
|
||||
<i className="fa fa-random" /> To <strong>{w.name}</strong>
|
||||
{isUnseen && (
|
||||
<Tooltip message="You haven't seen this workspace before" position="top">
|
||||
<i className="width-auto fa fa-asterisk surprise" />
|
||||
</Tooltip>
|
||||
)}
|
||||
</DropdownItem>
|
||||
);
|
||||
})}
|
||||
|
||||
<DropdownItem onClick={this._handleWorkspaceCreate}>
|
||||
<i className="fa fa-empty" /> Create Workspace
|
||||
</DropdownItem>
|
||||
|
||||
{missingRemoteProjects.length > 0 && (
|
||||
<DropdownDivider>
|
||||
Remote Workspaces{' '}
|
||||
<HelpTooltip>
|
||||
These workspaces have been shared with you via Insomnia Sync and do not yet exist on
|
||||
your machine.
|
||||
</HelpTooltip>
|
||||
</DropdownDivider>
|
||||
)}
|
||||
|
||||
{missingRemoteProjects.map(p => (
|
||||
<DropdownItem
|
||||
key={p.id}
|
||||
stayOpenAfterClick
|
||||
onClick={() => this._handlePullRemoteWorkspace(p)}>
|
||||
{pullingProjects[p.id] ? (
|
||||
<i className="fa fa-refresh fa-spin" />
|
||||
) : (
|
||||
<i className="fa fa-cloud-download" />
|
||||
)}
|
||||
Pull <strong>{p.name}</strong>
|
||||
</DropdownItem>
|
||||
))}
|
||||
|
||||
<DropdownDivider>
|
||||
{getAppName()} v{getAppVersion()}
|
||||
</DropdownDivider>
|
||||
|
@ -54,6 +54,7 @@ import {
|
||||
} from '../../sync/git/git-vcs';
|
||||
import { parseApiSpec } from '../../common/api-specs';
|
||||
import SettingsModal from './modals/settings-modal';
|
||||
import RemoteWorkspacesDropdown from './dropdowns/remote-workspaces-dropdown';
|
||||
|
||||
type Props = {|
|
||||
wrapperProps: WrapperProps,
|
||||
@ -294,7 +295,6 @@ class WrapperHome extends React.PureComponent<Props, State> {
|
||||
} else {
|
||||
handleSetActiveActivity(activeActivity);
|
||||
}
|
||||
|
||||
handleSetActiveWorkspace(id);
|
||||
}
|
||||
|
||||
@ -335,7 +335,7 @@ class WrapperHome extends React.PureComponent<Props, State> {
|
||||
|
||||
let log = <TimeFromNow timestamp={modifiedLocally} />;
|
||||
let branch = lastActiveBranch;
|
||||
if (apiSpec && lastCommitTime && apiSpec.modified > lastCommitTime) {
|
||||
if (w.scope === 'designer' && lastCommitTime && apiSpec?.modified > lastCommitTime) {
|
||||
// Show locally unsaved changes for spec
|
||||
// NOTE: this doesn't work for non-spec workspaces
|
||||
branch = lastActiveBranch + '*';
|
||||
@ -349,7 +349,7 @@ class WrapperHome extends React.PureComponent<Props, State> {
|
||||
branch = lastActiveBranch;
|
||||
log = (
|
||||
<React.Fragment>
|
||||
<TimeFromNow timestamp={lastCommitTime} /> by {lastCommitAuthor}
|
||||
<TimeFromNow timestamp={lastCommitTime} /> {lastCommitAuthor && `by ${lastCommitAuthor}`}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
@ -417,14 +417,16 @@ class WrapperHome extends React.PureComponent<Props, State> {
|
||||
);
|
||||
}
|
||||
|
||||
renderMenu() {
|
||||
renderCreateMenu() {
|
||||
const button = (
|
||||
<Button variant="contained" bg="surprise" className="margin-left">
|
||||
Create
|
||||
<i className="fa fa-caret-down pad-left-sm" />
|
||||
</Button>
|
||||
);
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
renderButton={() => (
|
||||
<Button variant="contained" bg="surprise" className="margin-left">
|
||||
Create <i className="fa fa-caret-down pad-left-sm" />
|
||||
</Button>
|
||||
)}>
|
||||
<Dropdown renderButton={button}>
|
||||
<DropdownDivider>New</DropdownDivider>
|
||||
<DropdownItem icon={<i className="fa fa-pencil" />} onClick={this._handleWorkspaceCreate}>
|
||||
Blank Document
|
||||
@ -448,6 +450,30 @@ class WrapperHome extends React.PureComponent<Props, State> {
|
||||
);
|
||||
}
|
||||
|
||||
renderDashboardMenu() {
|
||||
const { vcs, workspaces } = this.props.wrapperProps;
|
||||
return (
|
||||
<div className="row row--right pad-left wide">
|
||||
<div
|
||||
className="form-control form-control--outlined no-margin"
|
||||
style={{ maxWidth: '400px' }}>
|
||||
<KeydownBinder onKeydown={this._handleKeyDown}>
|
||||
<input
|
||||
ref={this._setFilterInputRef}
|
||||
type="text"
|
||||
placeholder="Filter..."
|
||||
onChange={this._handleFilterChange}
|
||||
className="no-margin"
|
||||
/>
|
||||
<span className="fa fa-search filter-icon" />
|
||||
</KeydownBinder>
|
||||
</div>
|
||||
{this.renderCreateMenu()}
|
||||
<RemoteWorkspacesDropdown vcs={vcs} workspaces={workspaces} className="margin-left" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { workspaces } = this.props.wrapperProps;
|
||||
const { filter } = this.state;
|
||||
@ -479,21 +505,7 @@ class WrapperHome extends React.PureComponent<Props, State> {
|
||||
<div className="document-listing__body pad-bottom">
|
||||
<div className="row-spaced margin-top margin-bottom-sm">
|
||||
<h2 className="no-margin">Dashboard</h2>
|
||||
<span className="row-spaced pad-left" style={{ maxWidth: '400px' }}>
|
||||
<div className="form-control form-control--outlined no-margin">
|
||||
<KeydownBinder onKeydown={this._handleKeyDown}>
|
||||
<input
|
||||
ref={this._setFilterInputRef}
|
||||
type="text"
|
||||
placeholder="Filter..."
|
||||
onChange={this._handleFilterChange}
|
||||
className="no-margin"
|
||||
/>
|
||||
<span className="fa fa-search filter-icon" />
|
||||
</KeydownBinder>
|
||||
</div>
|
||||
{this.renderMenu()}
|
||||
</span>
|
||||
{this.renderDashboardMenu()}
|
||||
</div>
|
||||
<CardContainer>{cards}</CardContainer>
|
||||
{filter && cards.length === 0 && (
|
||||
|
@ -474,6 +474,8 @@ class Dropdown extends PureComponent {
|
||||
}
|
||||
|
||||
const noResults = filter && filterItems && filterItems.length === 0;
|
||||
|
||||
const button = typeof renderButton === 'function' ? renderButton({ open }) : renderButton;
|
||||
return (
|
||||
<StyledDropdown
|
||||
style={style}
|
||||
@ -483,7 +485,7 @@ class Dropdown extends PureComponent {
|
||||
onKeyDown={this._handleKeyDown}
|
||||
tabIndex="-1"
|
||||
onMouseDown={Dropdown._handleMouseDown}>
|
||||
<React.Fragment key="button">{renderButton({ open })}</React.Fragment>
|
||||
<React.Fragment key="button">{button}</React.Fragment>
|
||||
{ReactDOM.createPortal(
|
||||
<div
|
||||
key="item"
|
||||
@ -517,7 +519,7 @@ class Dropdown extends PureComponent {
|
||||
Dropdown.propTypes = {
|
||||
// Required
|
||||
children: PropTypes.node.isRequired,
|
||||
renderButton: PropTypes.func.isRequired,
|
||||
renderButton: PropTypes.oneOfType([PropTypes.node, PropTypes.func]).isRequired,
|
||||
|
||||
// Optional
|
||||
right: PropTypes.bool,
|
||||
|
Loading…
Reference in New Issue
Block a user