Skip to content

Commit

Permalink
allow loading query from inside of query builder
Browse files Browse the repository at this point in the history
  • Loading branch information
YannanGao-gs committed Aug 24, 2022
1 parent 3fc5ab8 commit 0db500a
Show file tree
Hide file tree
Showing 7 changed files with 486 additions and 4 deletions.
5 changes: 5 additions & 0 deletions .changeset/giant-coats-lay.md
@@ -0,0 +1,5 @@
---
'@finos/legend-application-query': patch
---

Allow loading query from inside of query builder ([#1435](https://github.com/finos/legend-studio/issues/1435)).
4 changes: 4 additions & 0 deletions .changeset/proud-boxes-joke.md
@@ -0,0 +1,4 @@
---
'@finos/legend-application-query-bootstrap': patch
'@finos/legend-application-studio': patch
---
45 changes: 45 additions & 0 deletions packages/legend-application-query-bootstrap/style/index.scss
Expand Up @@ -247,6 +247,51 @@
}
}

// ---------------------------- Query Loader -------------------------------

.query-editor {
&__query-loader {
&__filter-section {
&__header__label {
color: var(--color-legacy-dark-grey-200);
}

&__section__toggler__btn {
svg {
color: var(--color-legacy-light-blue-100);
}
}

&__section__toggler__prompt {
color: var(--color-legacy-dark-grey-200);
}
}

&__search-section {
border-bottom: 0.1rem solid var(--color-legacy-light-grey-200);

&__input {
border: 0.1rem solid var(--color-legacy-light-grey-200);
}
}

&__body {
.table {
color: var(--color-legacy-dark-grey-200);
}

&__table__row:hover {
color: var(--color-legacy-light-blue-100);
background-color: var(--color-legacy-light-grey-200);
}

&__table__row--selected {
background-color: var(--color-legacy-light-grey-200);
}
}
}
}

// ---------------------------- Property Search -------------------------------

.query-builder-property-search-panel {
Expand Down
231 changes: 228 additions & 3 deletions packages/legend-application-query/src/components/QueryEditor.tsx
Expand Up @@ -15,6 +15,7 @@
*/

import {
type SelectComponent,
Dialog,
ArrowLeftIcon,
ExternalLinkSquareIcon,
Expand All @@ -25,10 +26,14 @@ import {
clsx,
EmptyLightBulbIcon,
LightBulbIcon,
SearchIcon,
TimesIcon,
CheckSquareIcon,
SquareIcon,
} from '@finos/legend-art';
import { getQueryParameters } from '@finos/legend-shared';
import { observer } from 'mobx-react-lite';
import { useEffect, useRef } from 'react';
import { useEffect, useRef, useState } from 'react';
import { DndProvider } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import { useParams } from 'react-router';
Expand All @@ -41,6 +46,7 @@ import {
LEGEND_QUERY_QUERY_PARAM_TOKEN,
LEGEND_QUERY_PATH_PARAM_TOKEN,
generateStudioProjectViewUrl,
generateExistingQueryEditorRoute,
} from '../stores/LegendQueryRouter.js';
import {
type QueryEditorStore,
Expand All @@ -50,19 +56,25 @@ import {
ServiceQueryEditorStore,
} from '../stores/QueryEditorStore.js';
import { QueryBuilder } from './QueryBuilder.js';
import { useApplicationStore } from '@finos/legend-application';
import {
type ApplicationStore,
useApplicationStore,
} from '@finos/legend-application';
import {
CreateQueryEditorStoreProvider,
ExistingQueryEditorStoreProvider,
ServiceQueryEditorStoreProvider,
useQueryEditorStore,
} from './QueryEditorStoreProvider.js';
import {
extractElementNameFromPath,
type LightQuery,
type RawLambda,
extractElementNameFromPath,
} from '@finos/legend-graph';
import { flowResult } from 'mobx';
import { useLegendQueryApplicationStore } from './LegendQueryBaseStoreProvider.js';
import type { LegendQueryApplicationConfig } from '../application/LegendQueryApplicationConfig.js';
import type { LegendQueryApplicationPlugin } from '../stores/LegendQueryApplicationPlugin.js';

const QueryExportDialogContent = observer(
(props: { exportState: QueryExportState }) => {
Expand Down Expand Up @@ -189,9 +201,209 @@ const renderQueryEditorHeaderLabel = (
return null;
};

const QueryLoader = observer(
(props: {
editorStore: QueryEditorStore;
applicationStore: ApplicationStore<
LegendQueryApplicationConfig,
LegendQueryApplicationPlugin
>;
}) => {
const { editorStore, applicationStore } = props;
const queryFinderRef = useRef<SelectComponent>(null);
const [selectedQueryID, setSelectedQueryID] = useState('');
const queryLoaderColumns = ['Name', 'Author', 'VersionID'];
const closeQueryImporter = (): void => {
editorStore.setIsQueryLoaderOpen(!editorStore.isQueryLoaderOpen);
};
const handleEnterQueryImporter = (): void =>
queryFinderRef.current?.focus();
const loadSelectedQuery = (): void => {
if (selectedQueryID) {
editorStore.setIsQueryLoaderOpen(!editorStore.isQueryLoaderOpen);
applicationStore.navigator.goTo(
generateExistingQueryEditorRoute(selectedQueryID),
);
window.location.reload();
} else {
applicationStore.notifyWarning(
'No query is selected. Please select a query to load or close.',
);
}
};
const buildQueryTableRow = (query: LightQuery): string[] => {
return [query.name, query.owner ?? 'anonymous', query.versionId];
};
const searchInputRef = useRef<HTMLInputElement>(null);
const searchQuery: React.ChangeEventHandler<HTMLInputElement> = (event) => {
editorStore.setSearchText(event.target.value);
if (editorStore.searchText.length >= 0) {
flowResult(editorStore.loadQueries(editorStore.searchText))
.then(() => editorStore.setIsLoadingQuery(false))
.catch(applicationStore.alertUnhandledError);
} else {
flowResult(editorStore.loadQueries(''))
.then(() => editorStore.setIsLoadingQuery(false))
.catch(applicationStore.alertUnhandledError);
}
};
const clearQuerySearching = (): void => {
editorStore.setSearchText('');
flowResult(editorStore.loadQueries(editorStore.searchText))
.then(() => editorStore.setIsLoadingQuery(false))
.catch(applicationStore.alertUnhandledError);
};
const toggleIsMineOnyly = (): void => {
editorStore.setIsMineOnly(!editorStore.isMineOnly);
};

return (
<Dialog
open={editorStore.isQueryLoaderOpen}
onClose={closeQueryImporter}
TransitionProps={{
onEnter: handleEnterQueryImporter,
}}
classes={{ container: 'search-modal__container' }}
PaperProps={{ classes: { root: 'search-modal__inner-container' } }}
>
<div className="modal modal--dark search-modal">
<div className="modal__title">Select a Query to Load</div>
<div className="query-editor__query-loader__filter-section">
<div className="query-editor__query-loader__filter-section__header__label">
Filter the queries
</div>
<div className="query-editor__query-loader__filter-section__section__toggler">
<button
className={clsx(
'query-editor__query-loader__filter-section__section__toggler__btn',
{
'query-editor__query-loader__filter-section__section__toggler__btn--toggled':
editorStore.isMineOnly,
},
)}
onClick={toggleIsMineOnyly}
tabIndex={-1}
>
{editorStore.isMineOnly ? <CheckSquareIcon /> : <SquareIcon />}
</button>
<div
className="query-editor__query-loader__filter-section__section__toggler__prompt"
onClick={toggleIsMineOnyly}
>
Mine Only
</div>
</div>
</div>
<div className="query-editor__query-loader__search-section">
<div className="query-editor__query-loader__search-section__input__container">
<input
ref={searchInputRef}
className={clsx(
'query-editor__query-loader__search-section__input input--dark',
{
'query-editor__query-loader__search-section__input--searching':
editorStore.searchText,
},
)}
onChange={searchQuery}
value={editorStore.searchText}
placeholder="Search a query by name"
/>
{!editorStore.searchText ? (
<div className="query-editor__query-loader__search-section__input__search__icon">
<SearchIcon />
</div>
) : (
<>
<button
className="query-editor__query-loader__search-section__input__clear-btn"
tabIndex={-1}
onClick={clearQuerySearching}
title="Clear"
>
<TimesIcon />
</button>
</>
)}
</div>
</div>
<div className="query-editor__query-loader__body">
{!editorStore.isLoadingQuery && (
<>
<table className="table">
<thead style={{ position: 'sticky', top: 0 }}>
<tr>
{queryLoaderColumns.map((column, idx) => (
// eslint-disable-next-line react/no-array-index-key
<th key={idx} className="table__cell--left">
{column}
</th>
))}
</tr>
</thead>
<tbody>
{(editorStore.isMineOnly
? editorStore.queries.filter((q) => q.isCurrentUserQuery)
: editorStore.queries
).map((q, rowIdx) => (
// eslint-disable-next-line react/no-array-index-key
<tr
key={q.id}
className={clsx(
'query-editor__query-loader__body__table__row',
{
'query-editor__query-loader__body__table__row--selected':
selectedQueryID === q.id,
},
)}
onClick={(event) => setSelectedQueryID(q.id)}
>
{buildQueryTableRow(q).map((value, idx) => (
// eslint-disable-next-line react/no-array-index-key
<td key={idx} className="table__cell--left">
{value}
</td>
))}
</tr>
))}
</tbody>
</table>
<div className="query-editor__query-loader__body__warning">
Show relevant queries by searching names
</div>
</>
)}
{editorStore.isLoadingQuery && (
<>
<PanelLoadingIndicator isLoading={editorStore.isLoadingQuery} />
<BlankPanelContent>loading queries...</BlankPanelContent>
</>
)}
</div>
<div className="search-modal__actions">
<button className="btn btn--dark" onClick={loadSelectedQuery}>
Load
</button>
<button className="btn btn--dark" onClick={closeQueryImporter}>
Close
</button>
</div>
</div>
</Dialog>
);
},
);

const QueryEditorHeaderContent = observer(() => {
const editorStore = useQueryEditorStore();
const applicationStore = useLegendQueryApplicationStore();
const loadQuery = (): void => {
editorStore.setIsQueryLoaderOpen(!editorStore.isQueryLoaderOpen);
flowResult(editorStore.loadQueries(''))
.then(() => editorStore.setIsLoadingQuery(false))
.catch(applicationStore.alertUnhandledError);
};
const viewQueryProject = (): void => {
const { groupId, artifactId, versionId } = editorStore.getProjectInfo();
applicationStore.navigator.openNewWindow(
Expand Down Expand Up @@ -228,6 +440,13 @@ const QueryEditorHeaderContent = observer(() => {
{renderQueryEditorHeaderLabel(editorStore)}
</div>
<div className="query-editor__header__actions">
<button
className="query-editor__header__action btn--dark"
tabIndex={-1}
onClick={loadQuery}
>
<div className="query-editor__header__action__label">Load Query</div>
</button>
<button
className="query-editor__header__action query-editor__header__action--simple btn--dark"
tabIndex={-1}
Expand All @@ -250,6 +469,12 @@ const QueryEditorHeaderContent = observer(() => {
)}
</button>
)}
{editorStore.isQueryLoaderOpen && (
<QueryLoader
editorStore={editorStore}
applicationStore={applicationStore}
/>
)}
<button
className="query-editor__header__action btn--dark"
tabIndex={-1}
Expand Down

0 comments on commit 0db500a

Please sign in to comment.