Modal prompt to select protofile (#2756)

This commit is contained in:
Opender Singh
2020-10-23 08:14:46 +13:00
committed by GitHub
parent bd4ed35dab
commit c73092e294
6 changed files with 222 additions and 9 deletions

View File

@@ -46,8 +46,8 @@ export function getById(_id: string): Promise<ProtoFile | null> {
return db.getWhere(type, { _id });
}
export function getByParentId(parentId: string): Promise<ProtoFile | null> {
return db.getWhere(type, { parentId });
export function findByParentId(parentId: string): Promise<Array<ProtoFile>> {
return db.find(type, { parentId });
}
export function all(): Promise<Array<ProtoFile>> {

View File

@@ -0,0 +1,121 @@
// @flow
import * as React from 'react';
import * as models from '../../../models';
import type { ProtoFile } from '../../../models/proto-file';
import ModalHeader from '../base/modal-header';
import ModalBody from '../base/modal-body';
import ModalFooter from '../base/modal-footer';
import autobind from 'autobind-decorator';
import type { Workspace } from '../../../models/workspace';
import Modal from '../base/modal';
import ProtoFileList from '../proto-file/proto-file-list';
type Props = {|
workspace: Workspace,
|};
type State = {|
protoFiles: Array<ProtoFile>,
selectedProtoFileId: string,
|};
type ProtoFilesModalOptions = {|
preselectProtoFileId?: string,
onSave: string => Promise<void>,
|};
const INITIAL_STATE: State = {
protoFiles: [],
selectedProtoFileId: '',
};
@autobind
class ProtoFilesModal extends React.PureComponent<Props, State> {
modal: Modal | null;
onSave: (string => Promise<void>) | null;
constructor(props: Props) {
super(props);
this.state = INITIAL_STATE;
this.onSave = null;
}
_setModalRef(ref: ?Modal) {
this.modal = ref;
}
async show(options: ProtoFilesModalOptions) {
this.onSave = options.onSave;
this.setState({ ...INITIAL_STATE });
this.modal && this.modal.show();
await this._refresh(options.preselectProtoFileId);
}
async _refresh(preselectProtoFileId?: string) {
const { workspace } = this.props;
const protoFilesForWorkspace = await models.protoFile.findByParentId(workspace._id);
protoFilesForWorkspace.push(
{ _id: 'pf_123', name: 'File 1' },
{ _id: 'pf_456', name: 'File 2' },
{ _id: 'pf_789', name: 'File 3' },
);
this.setState({
protoFiles: protoFilesForWorkspace,
selectedProtoFileId: preselectProtoFileId,
});
}
async _handleSave(e: SyntheticEvent<HTMLButtonElement>) {
e.preventDefault();
this.hide();
if (typeof this.onSave === 'function') {
await this.onSave(this.state.selectedProtoFileId);
}
}
hide() {
this.modal && this.modal.hide();
}
_handleSelect(id: string) {
this.setState({ selectedProtoFileId: id });
}
_handleDelete(protoFile: ProtoFile) {
// TODO: to be built in INS-209
console.log(`delete ${protoFile._id}`);
}
render() {
const { protoFiles, selectedProtoFileId } = this.state;
return (
<Modal ref={this._setModalRef}>
<ModalHeader>Select Protofile</ModalHeader>
<ModalBody className="wide pad">
Files
<ProtoFileList
protoFiles={protoFiles}
selectedId={selectedProtoFileId}
handleSelect={this._handleSelect}
handleDelete={this._handleDelete}
/>
</ModalBody>
<ModalFooter>
<div>
<button className="btn" onClick={this._handleSave} disabled={!selectedProtoFileId}>
Save
</button>
</div>
</ModalFooter>
</Modal>
);
}
}
export default ProtoFilesModal;

View File

@@ -17,7 +17,8 @@ import {
} from '../../../common/constants';
import * as models from '../../../models/index';
import { trackEvent } from '../../../common/analytics';
import { showAlert } from './index';
import { showModal } from './index';
import ProtoFilesModal from './proto-files-modal';
type RequestCreateModalOptions = {
parentId: string,
@@ -54,13 +55,15 @@ class RequestCreateModal extends PureComponent {
const { parentId, selectedContentType, selectedMethod } = this.state;
const requestName = this._input.value;
if (selectedMethod === METHOD_GRPC) {
// TODO: Create new grpc request with the name - INS-198
showModal(ProtoFilesModal, {
onSave: async protofileId => {
// TODO: Create new grpc request with the name and selected proto file - INS-198
console.log(`Create request name: [${requestName}], and protofileId: [${protofileId}]`);
const id = 'gr_123';
this._onComplete(id);
// TODO: Show protofile selection modal - INS-188
showAlert({ title: 'Select protofile', message: 'To be built', okLabel: 'ok' });
const createdRequestId = 'gr_123';
this._onComplete(createdRequestId);
},
});
} else {
const request = await models.initModel(models.request.type, {
parentId,

View File

@@ -0,0 +1,54 @@
// @flow
import React from 'react';
import styled from 'styled-components';
import type { ProtoFile } from '../../../models/proto-file';
import PromptButton from '../base/prompt-button';
import type { DeleteProtoFileHandler, SelectProtoFileHandler } from './proto-file-list';
import { ListGroupItem } from '../../../../../insomnia-components';
type Props = {
protoFile: ProtoFile,
isSelected?: boolean,
handleSelect: SelectProtoFileHandler,
handleDelete: DeleteProtoFileHandler,
};
const SelectableListItem: React.PureComponent<{ isSelected?: boolean }> = styled(ListGroupItem)`
&:hover {
background-color: var(--hl-sm) !important;
}
background-color: ${({ isSelected }) => isSelected && 'var(--hl-sm) !important'};
`;
const ProtoFileListItem = ({ protoFile, isSelected, handleSelect, handleDelete }: Props) => {
const { name, _id } = protoFile;
// Don't re-instantiate the callbacks if the dependencies have not changed
const handleSelectCallback = React.useCallback(() => handleSelect(_id), [handleSelect, _id]);
const handleDeleteCallback = React.useCallback(
async (e: SyntheticEvent<HTMLButtonElement>) => {
e.stopPropagation();
await handleDelete(protoFile);
},
[handleDelete, protoFile],
);
return (
<SelectableListItem isSelected={isSelected} onClick={handleSelectCallback}>
<div className="row-spaced">
{name}
<PromptButton
className="btn btn--super-compact btn--outlined"
addIcon
confirmMessage=""
onClick={handleDeleteCallback}
title="Delete Proto File">
<i className="fa fa-trash-o" />
</PromptButton>
</div>
</SelectableListItem>
);
};
export default ProtoFileListItem;

View File

@@ -0,0 +1,32 @@
// @flow
import React from 'react';
import type { ProtoFile } from '../../../models/proto-file';
import { ListGroup, ListGroupItem } from 'insomnia-components';
import ProtoFileListItem from './proto-file-list-item';
export type SelectProtoFileHandler = (id: string) => void;
export type DeleteProtoFileHandler = (protofile: ProtoFile) => Promise<void>;
type Props = {
protoFiles: Array<ProtoFile>,
selectedId?: string,
handleSelect: SelectProtoFileHandler,
handleDelete: DeleteProtoFileHandler,
};
const ProtoFileList = ({ protoFiles, selectedId, handleSelect, handleDelete }: Props) => (
<ListGroup>
{!protoFiles.length && <ListGroupItem>No proto files exist for this workspace</ListGroupItem>}
{protoFiles.map(p => (
<ProtoFileListItem
key={p.id}
protoFile={p}
isSelected={p._id === selectedId}
handleSelect={handleSelect}
handleDelete={handleDelete}
/>
))}
</ListGroup>
);
export default ProtoFileList;

View File

@@ -90,6 +90,7 @@ import {
getAppName,
} from '../../common/constants';
import { Spectral } from '@stoplight/spectral';
import ProtoFilesModal from './modals/proto-files-modal';
const spectral = new Spectral();
@@ -770,6 +771,8 @@ class Wrapper extends React.PureComponent<WrapperProps, State> {
childObjects={sidebarChildren.all}
handleExportRequestsToFile={handleExportRequestsToFile}
/>
<ProtoFilesModal ref={registerModal} workspace={activeWorkspace} />
</ErrorBoundary>
</div>
<React.Fragment key={`views::${this.state.activeGitBranch}`}>