Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes for Analyzers Web UI #14739

Merged
merged 22 commits into from
Sep 10, 2021
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -200,13 +200,6 @@
active: false
}
],
analyzers: [
{
name: 'Analyzers',
route: 'analyzers',
active: true
}
],
queries: [
{
name: 'Editor',
Expand Down
1 change: 0 additions & 1 deletion js/apps/system/_admin/aardvark/APP/react/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ require('nvd3');

// parse prometheus
const parsePrometheusTextFormat = require('parse-prometheus-text-format');
window.parsePrometheusTextFormat = parsePrometheusTextFormat;

// import new react views
// require('./views/shards/ShardsReactView');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,69 @@ interface ModalBodyProps {
children?: ReactNode;
innerStyle?: CSSProperties;
maximize?: boolean;
show?: boolean;
}

const ModalBody = ({ children, innerStyle = {}, maximize = false }: ModalBodyProps) => {
function getBorderAndMarginHeightOffset (style: CSSStyleDeclaration) {
let offset = 0;

offset += parseFloat(style.marginTop) || 0;
offset += parseFloat(style.marginBottom) || 0;
offset += parseFloat(style.borderTop) || 0;
offset += parseFloat(style.borderBottom) || 0;

return offset;
}

function getPaddingHeightOffset (style: CSSStyleDeclaration) {
let offset = 0;

offset += parseFloat(style.paddingTop) || 0;
offset += parseFloat(style.paddingBottom) || 0;

return offset;
}

const ModalBody = ({ children, innerStyle = {}, maximize = false, show = false }: ModalBodyProps) => {
const innerRef = useRef(null);

useEffect(() => {
if (maximize) {
if (maximize && show) {
const body = innerRef.current as unknown as HTMLDivElement;
const parent = body.parentElement as HTMLElement;
const header = parent.firstElementChild as HTMLElement;
const footer = parent.lastElementChild as HTMLElement;
const maxParentHeight = window.innerHeight * 0.9;
const bodyHeight = maxParentHeight - header.offsetHeight - footer.offsetHeight;
const grandParent = parent.parentElement as HTMLElement;
const gpStyle = window.getComputedStyle(grandParent);
const pStyle = window.getComputedStyle(parent);

let offset = 0;
offset += getPaddingHeightOffset(gpStyle);
offset += getBorderAndMarginHeightOffset(pStyle);
offset += getPaddingHeightOffset(pStyle);

parent.childNodes.forEach(child => {
const htmlChild = child as HTMLElement;
const hcStyle = window.getComputedStyle(htmlChild);

if (child === body) {
offset += getPaddingHeightOffset(hcStyle);
} else {
offset += htmlChild.clientHeight;
}

offset += getBorderAndMarginHeightOffset(hcStyle);
});

const maxGrandParentHeight = window.innerHeight * 0.9;
const bodyHeight = Math.floor(maxGrandParentHeight - offset);

body.style.height = `${bodyHeight}px`;
body.style.maxHeight = 'unset';
grandParent.style.height = gpStyle.maxHeight;
grandParent.style.overflowY = 'hidden';
}
}, [maximize]);
}, [maximize, show]);

const style = Object.assign({ minWidth: '50vw' }, innerStyle);
const style = Object.assign({ minWidth: '60vw' }, innerStyle);

return <div className="modal-body" style={style} ref={innerRef}>
{children}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import PlainLabel from "./PlainLabel";

const StyledTextarea = styled.textarea`
width: 90%;
resize: vertical;
`;

type TextareaProps = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ const ViewButton = ({ analyzer }: ButtonProps) => {
{showJsonForm ? 'Switch to form view' : 'Switch to code view'}
</button>
</ModalHeader>
<ModalBody maximize={true}>
<ModalBody maximize={true} show={show}>
<Grid>
{
showJsonForm
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ const AddAnalyzer = ({ analyzers }: AddAnalyzerProps) => {
</Cell>
</Grid>
</ModalHeader>
<ModalBody maximize={true}>
<ModalBody maximize={true} show={state.show}>
<Grid>
{
state.showJsonForm
Expand Down Expand Up @@ -199,7 +199,10 @@ const AddAnalyzer = ({ analyzers }: AddAnalyzerProps) => {
</ModalBody>
<ModalFooter>
<button className="button-close" onClick={() => dispatch({ type: 'reset' })}>Close</button>
<button className="button-success" style={{ float: 'right' }} onClick={handleAdd}>Create</button>
<button className="button-success" style={{ float: 'right' }} onClick={handleAdd}
disabled={state.lockJsonForm}>
Create
</button>
</ModalFooter>
</Modal>
</>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* global arangoHelper, frontendConfig */
/* global arangoHelper, frontendConfig, $ */

import { isEqual, sortBy } from 'lodash';
import { isEqual, map, sortBy } from 'lodash';
import minimatch from 'minimatch';
import React, { useCallback, useEffect, useState } from 'react';
import useSWR from 'swr';
Expand All @@ -23,11 +23,9 @@ const FilterHelpModal = () => {
return <>
<a href={'#analyzers'} onClick={showFilterHelp}>
<i className={'fa fa-question-circle'} style={{
float: 'right',
marginRight: 10,
marginTop: 3,
color: '#fff',
fontSize: '16pt'
marginTop: 5,
color: 'rgb(85, 85, 85)',
fontSize: '18px'
}}/>
</a>
<Modal show={show} setShow={setShow}>
Expand Down Expand Up @@ -66,6 +64,11 @@ const FilterHelpModal = () => {
</>;
};

const toggleHeaderDropdown = () => {
$('#analyzersToggle').toggleClass('activated');
$('#analyzersDropdown2').slideToggle(200);
};

const AnalyzersReactView = () => {
const { data } = useSWR('/analyzer', (path) => getApiRouteForCurrentDB().get(path));
const { data: permData } = useSWR(
Expand Down Expand Up @@ -125,89 +128,92 @@ const AnalyzersReactView = () => {
if (data && permData) {
const permission = permData.body.result;

if (!isEqual(data.body.result, analyzers)) {
if (!isEqual(map(data.body.result, 'name'), map(analyzers, 'name'))) {
setAnalyzers(data.body.result);
processAndSetFilteredAnalyzers(data.body.result);
}

return <div className={'innerContent'} id={'analyzersContent'} style={{ paddingTop: 0 }}>
<Grid>
<Cell size={'1'}>
<Grid className={'sectionHeader'}>
<Cell size={'2-5'}>
<div className={'title'}><AddAnalyzer analyzers={analyzers}/></div>
</Cell>

<Cell size={'3-5'}>
return <>
<div className="headerBar">
<div className="search-field">
<input type={'text'} id={'searchInput'} className={'search-input'} value={filterExpr}
onChange={getChangeHandler(setFilterExpr)} placeholder={'Filter...'}/>
<i id="searchSubmit" className="fa fa-search"/>
</div>
<div className="headerButtonBar">
<ul className="headerButtonList">
<li className="enabled">
<FilterHelpModal/>
<label htmlFor={'filter-input'} style={{
color: '#fff',
marginRight: 10,
float: 'right'
}}>
Filter: <input type={'text'} id={'filter-input'} className={'search-input'}
value={filterExpr} onChange={getChangeHandler(setFilterExpr)}
placeholder={'<glob>|(<db|name|type>:<glob> )+'}
style={{
margin: 0,
width: 300,
paddingLeft: 25
}}/>
<i className={'fa fa-filter'} style={{
position: 'relative',
float: 'left',
top: 9,
left: 60,
cursor: 'default',
color: 'rgb(85, 85, 85)'
}}/>
</label>
<label htmlFor={'inbuilt-analyzers'} className="pure-checkbox" style={{
float: 'right',
color: '#fff',
marginTop: 3
}}>
<input id={'inbuilt-analyzers'} type={'checkbox'} checked={showInbuiltAnalyzers}
onChange={toggleInbuiltAnalyzers} style={{
width: 'auto',
marginBottom: 7
}}/> Show In-built Analyzers
</label>
</Cell>
</Grid>
<table className={'arango-table'}>
<thead>
<tr>
<th className={'arango-table-th table-cell0'}>DB</th>
<th className={'arango-table-th table-cell1'}>Name</th>
<th className={'arango-table-th table-cell2'}>Type</th>
<th className={'arango-table-th table-cell3'}>Actions</th>
</tr>
</thead>
<tbody>
{
filteredAnalyzers.length
? filteredAnalyzers.map(analyzer => (
<tr key={analyzer.name}>
<td className={'arango-table-td table-cell0'}>{analyzer.db}</td>
<td className={'arango-table-td table-cell1'}>{analyzer.name}</td>
<td className={'arango-table-td table-cell2'}>{typeNameMap[analyzer.type]}</td>
<td className={'arango-table-td table-cell3'}>
<Actions analyzer={analyzer} permission={permission}/>
</li>
<li className="enabled">
<a id="analyzersToggle" className="headerButton" href={'#analyzers'}
onClick={toggleHeaderDropdown}>
<span className="icon_arangodb_settings2" title="Settings"/>
</a>
</li>
</ul>
</div>
</div>

<div id="analyzersDropdown2" className="headerDropdown">
<div id="analyzersDropdown" className="dropdownInner">
<ul>
<li className="nav-header">System</li>
<li>
<a href={'#analyzers'}>
<label className="checkbox checkboxLabel">
<input className="css-checkbox" type="checkbox"
onChange={toggleInbuiltAnalyzers}/>
<i
className={`fa ${showInbuiltAnalyzers ? 'fa-check-square-o' : 'fa-square-o'}`}/>
Built-in
</label>
</a>
</li>
</ul>
</div>
</div>

<div className={'contentDiv'} id={'analyzersContent'} style={{ paddingTop: 0 }}>
<Grid>
<Cell size={'1'}>
<div className={'sectionHeader'}>
<div className={'title'}><AddAnalyzer analyzers={analyzers}/></div>
</div>
<table className={'arango-table'}>
<thead>
<tr>
<th className={'arango-table-th table-cell0'}>DB</th>
<th className={'arango-table-th table-cell1'}>Name</th>
<th className={'arango-table-th table-cell2'}>Type</th>
<th className={'arango-table-th table-cell3'}>Actions</th>
</tr>
</thead>
<tbody>
{
filteredAnalyzers.length
? filteredAnalyzers.map(analyzer => (
<tr key={analyzer.name}>
<td className={'arango-table-td table-cell0'}>{analyzer.db}</td>
<td className={'arango-table-td table-cell1'}>{analyzer.name}</td>
<td className={'arango-table-td table-cell2'}>{typeNameMap[analyzer.type]}</td>
<td className={'arango-table-td table-cell3'}>
<Actions analyzer={analyzer} permission={permission}/>
</td>
</tr>
))
: <tr>
<td className={'arango-table-td table-cell0'} colSpan={4}>
No analyzers found.
</td>
</tr>
))
: <tr>
<td className={'arango-table-td table-cell0'} colSpan={4}>
No analyzers found.
</td>
</tr>
}
</tbody>
</table>
</Cell>
</Grid>
</div>;
}
</tbody>
</table>
</Cell>
</Grid>
</div>
</>;
}

return <h1>Analyzers</h1>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,8 @@ const baseSchema = {
properties: {
name: {
nullable: false,
type: 'string'
type: 'string',
pattern: '^([a-zA-Z][a-zA-Z0-9-_]*)?$'
},
features: {
type: 'array',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,37 +55,37 @@ const AqlForm = ({ formState, dispatch, disabled }: FormProps) => {
const aqlFormState = formState as AqlState;

return <Grid>
<Cell size={'1-3'}>
<Cell size={'1-2'}>
<Textarea label={'Query String'} value={aqlFormState.properties.queryString || ''} disabled={disabled}
onChange={updateQueryString} rows={4} required={true}/>
</Cell>

<Cell size={'1-3'}>
<Cell size={'1-2'}>
<Grid>
<Cell size={'1'}>
<Textbox label={'Batch Size'} type={'number'} min={1} placeholder={'10'} required={true}
<Textbox label={'Batch Size'} type={'number'} min={1} required={true}
value={aqlFormState.properties.batchSize || ''} disabled={disabled}
onChange={getNumericFieldSetter('properties.batchSize')}/>
</Cell>
<Cell size={'1'}>
<Textbox label={'Memory Limit'} type={'number'} min={1} max={33554432} required={true}
placeholder={'1048576'} value={aqlFormState.properties.memoryLimit || ''}
value={aqlFormState.properties.memoryLimit || ''}
disabled={disabled} onChange={getNumericFieldSetter('properties.memoryLimit')}/>
</Cell>
</Grid>
</Cell>

<Cell size={'1-3'}>
<Cell size={'1-2'}>
<Grid>
<Cell size={'1-2'}>
<Cell size={'1-3'}>
<Checkbox checked={aqlFormState.properties.collapsePositions || false} disabled={disabled}
onChange={updateCollapsePositions} label={'Collapse Positions'}/>
</Cell>
<Cell size={'1-2'}>
<Cell size={'1-3'}>
<Checkbox checked={aqlFormState.properties.keepNull || false} onChange={updateKeepNull}
disabled={disabled} label={'Keep Null'}/>
</Cell>
<Cell size={'1'}>
<Cell size={'1-3'}>
<Select label={'Return Type'} value={aqlFormState.properties.returnType || 'string'}
onChange={updateReturnType} required={true} disabled={disabled}>
<option value={'string'}>String</option>
Expand Down
Loading