Merge pull request #93 from tiqi-group/45-placing-the-explanation-question-mark-next-to-the-variable-instead-of-above

feat: placing the explanation question mark next to the variable instead of above
This commit is contained in:
Mose Müller 2024-01-16 14:16:38 +01:00 committed by GitHub
commit 14601105a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 75 additions and 58 deletions

View File

@ -118,7 +118,7 @@ import { ChevronDown, ChevronRight } from 'react-bootstrap-icons';
import { getIdFromFullAccessPath } from '../utils/stringUtils'; import { getIdFromFullAccessPath } from '../utils/stringUtils';
import { LevelName } from './NotificationsComponent'; import { LevelName } from './NotificationsComponent';
interface ImageComponentProps { type ImageComponentProps = {
name: string; name: string;
parentPath?: string; parentPath?: string;
readOnly: boolean; readOnly: boolean;
@ -165,14 +165,15 @@ export const ImageComponent = React.memo((props: ImageComponentProps) => {
onClick={() => setOpen(!open)} onClick={() => setOpen(!open)}
style={{ cursor: 'pointer' }} // Change cursor style on hover style={{ cursor: 'pointer' }} // Change cursor style on hover
> >
{displayName} {open ? <ChevronDown /> : <ChevronRight />} {displayName}
<DocStringComponent docString={docString} />
{open ? <ChevronDown /> : <ChevronRight />}
</Card.Header> </Card.Header>
<Collapse in={open}> <Collapse in={open}>
<Card.Body> <Card.Body>
{process.env.NODE_ENV === 'development' && ( {process.env.NODE_ENV === 'development' && (
<p>Render count: {renderCount.current}</p> <p>Render count: {renderCount.current}</p>
)} )}
<DocStringComponent docString={docString} />
{/* Your component TSX here */} {/* Your component TSX here */}
</Card.Body> </Card.Body>
</Collapse> </Collapse>

View File

@ -6,7 +6,7 @@ import { getIdFromFullAccessPath } from '../utils/stringUtils';
import { LevelName } from './NotificationsComponent'; import { LevelName } from './NotificationsComponent';
import { WebSettingsContext } from '../WebSettings'; import { WebSettingsContext } from '../WebSettings';
interface AsyncMethodProps { type AsyncMethodProps = {
name: string; name: string;
parentPath: string; parentPath: string;
parameters: Record<string, string>; parameters: Record<string, string>;
@ -14,7 +14,7 @@ interface AsyncMethodProps {
docString?: string; docString?: string;
hideOutput?: boolean; hideOutput?: boolean;
addNotification: (message: string, levelname?: LevelName) => void; addNotification: (message: string, levelname?: LevelName) => void;
} };
export const AsyncMethodComponent = React.memo((props: AsyncMethodProps) => { export const AsyncMethodComponent = React.memo((props: AsyncMethodProps) => {
const { name, parentPath, docString, value: runningTask, addNotification } = props; const { name, parentPath, docString, value: runningTask, addNotification } = props;
@ -102,14 +102,12 @@ export const AsyncMethodComponent = React.memo((props: AsyncMethodProps) => {
{process.env.NODE_ENV === 'development' && ( {process.env.NODE_ENV === 'development' && (
<div>Render count: {renderCount.current}</div> <div>Render count: {renderCount.current}</div>
)} )}
<h5> <h5>Function: {displayName}</h5>
Function: {displayName}
<DocStringComponent docString={docString} />
</h5>
<Form onSubmit={execute} ref={formRef}> <Form onSubmit={execute} ref={formRef}>
{args} {args}
<Button id={`button-${id}`} name={name} value={parentPath} type="submit"> <Button id={`button-${id}`} name={name} value={parentPath} type="submit">
{runningTask ? 'Stop' : 'Start'} {runningTask ? 'Stop ' : 'Start '}
<DocStringComponent docString={docString} />
</Button> </Button>
</Form> </Form>
</div> </div>

View File

@ -6,7 +6,7 @@ import { DocStringComponent } from './DocStringComponent';
import { getIdFromFullAccessPath } from '../utils/stringUtils'; import { getIdFromFullAccessPath } from '../utils/stringUtils';
import { LevelName } from './NotificationsComponent'; import { LevelName } from './NotificationsComponent';
interface ButtonComponentProps { type ButtonComponentProps = {
name: string; name: string;
parentPath?: string; parentPath?: string;
value: boolean; value: boolean;
@ -14,7 +14,7 @@ interface ButtonComponentProps {
docString: string; docString: string;
mapping?: [string, string]; // Enforce a tuple of two strings mapping?: [string, string]; // Enforce a tuple of two strings
addNotification: (message: string, levelname?: LevelName) => void; addNotification: (message: string, levelname?: LevelName) => void;
} };
export const ButtonComponent = React.memo((props: ButtonComponentProps) => { export const ButtonComponent = React.memo((props: ButtonComponentProps) => {
const { name, parentPath, value, readOnly, docString, addNotification } = props; const { name, parentPath, value, readOnly, docString, addNotification } = props;
@ -48,7 +48,6 @@ export const ButtonComponent = React.memo((props: ButtonComponentProps) => {
<div>Render count: {renderCount.current}</div> <div>Render count: {renderCount.current}</div>
)} )}
<DocStringComponent docString={docString} />
<ToggleButton <ToggleButton
id={`toggle-check-${id}`} id={`toggle-check-${id}`}
type="checkbox" type="checkbox"
@ -58,6 +57,7 @@ export const ButtonComponent = React.memo((props: ButtonComponentProps) => {
disabled={readOnly} disabled={readOnly}
onChange={(e) => setChecked(e.currentTarget.checked)}> onChange={(e) => setChecked(e.currentTarget.checked)}>
{displayName} {displayName}
<DocStringComponent docString={docString} />
</ToggleButton> </ToggleButton>
</div> </div>
); );

View File

@ -6,7 +6,7 @@ import { DocStringComponent } from './DocStringComponent';
import { getIdFromFullAccessPath } from '../utils/stringUtils'; import { getIdFromFullAccessPath } from '../utils/stringUtils';
import { LevelName } from './NotificationsComponent'; import { LevelName } from './NotificationsComponent';
interface ColouredEnumComponentProps { type ColouredEnumComponentProps = {
name: string; name: string;
parentPath: string; parentPath: string;
value: string; value: string;
@ -14,7 +14,7 @@ interface ColouredEnumComponentProps {
readOnly: boolean; readOnly: boolean;
enumDict: Record<string, string>; enumDict: Record<string, string>;
addNotification: (message: string, levelname?: LevelName) => void; addNotification: (message: string, levelname?: LevelName) => void;
} };
export const ColouredEnumComponent = React.memo((props: ColouredEnumComponentProps) => { export const ColouredEnumComponent = React.memo((props: ColouredEnumComponentProps) => {
const { const {
@ -53,10 +53,12 @@ export const ColouredEnumComponent = React.memo((props: ColouredEnumComponentPro
{process.env.NODE_ENV === 'development' && ( {process.env.NODE_ENV === 'development' && (
<div>Render count: {renderCount.current}</div> <div>Render count: {renderCount.current}</div>
)} )}
<DocStringComponent docString={docString} />
<Row> <Row>
<Col className="d-flex align-items-center"> <Col className="d-flex align-items-center">
<InputGroup.Text>{displayName}</InputGroup.Text> <InputGroup.Text>
{displayName}
<DocStringComponent docString={docString} />
</InputGroup.Text>
{readOnly ? ( {readOnly ? (
// Display the Form.Control when readOnly is true // Display the Form.Control when readOnly is true
<Form.Control <Form.Control

View File

@ -1,9 +1,9 @@
import { Badge, Tooltip, OverlayTrigger } from 'react-bootstrap'; import { Badge, Tooltip, OverlayTrigger } from 'react-bootstrap';
import React from 'react'; import React from 'react';
interface DocStringProps { type DocStringProps = {
docString?: string; docString?: string;
} };
export const DocStringComponent = React.memo((props: DocStringProps) => { export const DocStringComponent = React.memo((props: DocStringProps) => {
const { docString } = props; const { docString } = props;

View File

@ -6,14 +6,14 @@ import { getIdFromFullAccessPath } from '../utils/stringUtils';
import { DocStringComponent } from './DocStringComponent'; import { DocStringComponent } from './DocStringComponent';
import { LevelName } from './NotificationsComponent'; import { LevelName } from './NotificationsComponent';
interface EnumComponentProps { type EnumComponentProps = {
name: string; name: string;
parentPath: string; parentPath: string;
value: string; value: string;
docString?: string; docString?: string;
enumDict: Record<string, string>; enumDict: Record<string, string>;
addNotification: (message: string, levelname?: LevelName) => void; addNotification: (message: string, levelname?: LevelName) => void;
} };
export const EnumComponent = React.memo((props: EnumComponentProps) => { export const EnumComponent = React.memo((props: EnumComponentProps) => {
const { const {
@ -52,10 +52,12 @@ export const EnumComponent = React.memo((props: EnumComponentProps) => {
{process.env.NODE_ENV === 'development' && ( {process.env.NODE_ENV === 'development' && (
<div>Render count: {renderCount.current}</div> <div>Render count: {renderCount.current}</div>
)} )}
<DocStringComponent docString={docString} />
<Row> <Row>
<Col className="d-flex align-items-center"> <Col className="d-flex align-items-center">
<InputGroup.Text>{displayName}</InputGroup.Text> <InputGroup.Text>
{displayName}
<DocStringComponent docString={docString} />
</InputGroup.Text>
<Form.Select <Form.Select
aria-label="Default select example" aria-label="Default select example"
value={value} value={value}

View File

@ -27,7 +27,7 @@ type AttributeType =
| 'ColouredEnum'; | 'ColouredEnum';
type ValueType = boolean | string | number | object; type ValueType = boolean | string | number | object;
export interface Attribute { export type Attribute = {
type: AttributeType; type: AttributeType;
value?: ValueType | ValueType[]; value?: ValueType | ValueType[];
readonly: boolean; readonly: boolean;
@ -35,7 +35,7 @@ export interface Attribute {
parameters?: Record<string, string>; parameters?: Record<string, string>;
async?: boolean; async?: boolean;
enum?: Record<string, string>; enum?: Record<string, string>;
} };
type GenericComponentProps = { type GenericComponentProps = {
attribute: Attribute; attribute: Attribute;
name: string; name: string;
@ -95,7 +95,7 @@ export const GenericComponent = React.memo(
<SliderComponent <SliderComponent
name={name} name={name}
parentPath={parentPath} parentPath={parentPath}
docString={attribute.doc} docString={attribute.value['value'].doc}
readOnly={attribute.readonly} readOnly={attribute.readonly}
value={attribute.value['value']} value={attribute.value['value']}
min={attribute.value['min']} min={attribute.value['min']}
@ -179,7 +179,7 @@ export const GenericComponent = React.memo(
parentPath={parentPath} parentPath={parentPath}
value={attribute.value['value']['value'] as string} value={attribute.value['value']['value'] as string}
readOnly={attribute.readonly} readOnly={attribute.readonly}
docString={attribute.doc} docString={attribute.value['value'].doc}
// Add any other specific props for the ImageComponent here // Add any other specific props for the ImageComponent here
format={attribute.value['format']['value'] as string} format={attribute.value['format']['value'] as string}
addNotification={addNotification} addNotification={addNotification}

View File

@ -6,7 +6,7 @@ import { ChevronDown, ChevronRight } from 'react-bootstrap-icons';
import { getIdFromFullAccessPath } from '../utils/stringUtils'; import { getIdFromFullAccessPath } from '../utils/stringUtils';
import { LevelName } from './NotificationsComponent'; import { LevelName } from './NotificationsComponent';
interface ImageComponentProps { type ImageComponentProps = {
name: string; name: string;
parentPath: string; parentPath: string;
value: string; value: string;
@ -14,7 +14,7 @@ interface ImageComponentProps {
docString: string; docString: string;
format: string; format: string;
addNotification: (message: string, levelname?: LevelName) => void; addNotification: (message: string, levelname?: LevelName) => void;
} };
export const ImageComponent = React.memo((props: ImageComponentProps) => { export const ImageComponent = React.memo((props: ImageComponentProps) => {
const { name, parentPath, value, docString, format, addNotification } = props; const { name, parentPath, value, docString, format, addNotification } = props;
@ -48,14 +48,15 @@ export const ImageComponent = React.memo((props: ImageComponentProps) => {
onClick={() => setOpen(!open)} onClick={() => setOpen(!open)}
style={{ cursor: 'pointer' }} // Change cursor style on hover style={{ cursor: 'pointer' }} // Change cursor style on hover
> >
{displayName} {open ? <ChevronDown /> : <ChevronRight />} {displayName}
<DocStringComponent docString={docString} />
{open ? <ChevronDown /> : <ChevronRight />}
</Card.Header> </Card.Header>
<Collapse in={open}> <Collapse in={open}>
<Card.Body> <Card.Body>
{process.env.NODE_ENV === 'development' && ( {process.env.NODE_ENV === 'development' && (
<p>Render count: {renderCount.current}</p> <p>Render count: {renderCount.current}</p>
)} )}
<DocStringComponent docString={docString} />
{/* Your component JSX here */} {/* Your component JSX here */}
{format === '' && value === '' ? ( {format === '' && value === '' ? (
<p>No image set in the backend.</p> <p>No image set in the backend.</p>

View File

@ -4,14 +4,14 @@ import { Attribute, GenericComponent } from './GenericComponent';
import { getIdFromFullAccessPath } from '../utils/stringUtils'; import { getIdFromFullAccessPath } from '../utils/stringUtils';
import { LevelName } from './NotificationsComponent'; import { LevelName } from './NotificationsComponent';
interface ListComponentProps { type ListComponentProps = {
name: string; name: string;
parentPath?: string; parentPath?: string;
value: Attribute[]; value: Attribute[];
docString: string; docString: string;
isInstantUpdate: boolean; isInstantUpdate: boolean;
addNotification: (message: string, levelname?: LevelName) => void; addNotification: (message: string, levelname?: LevelName) => void;
} };
export const ListComponent = React.memo((props: ListComponentProps) => { export const ListComponent = React.memo((props: ListComponentProps) => {
const { name, parentPath, value, docString, isInstantUpdate, addNotification } = const { name, parentPath, value, docString, isInstantUpdate, addNotification } =

View File

@ -6,14 +6,14 @@ import { DocStringComponent } from './DocStringComponent';
import { getIdFromFullAccessPath } from '../utils/stringUtils'; import { getIdFromFullAccessPath } from '../utils/stringUtils';
import { LevelName } from './NotificationsComponent'; import { LevelName } from './NotificationsComponent';
interface MethodProps { type MethodProps = {
name: string; name: string;
parentPath: string; parentPath: string;
parameters: Record<string, string>; parameters: Record<string, string>;
docString?: string; docString?: string;
hideOutput?: boolean; hideOutput?: boolean;
addNotification: (message: string, levelname?: LevelName) => void; addNotification: (message: string, levelname?: LevelName) => void;
} };
export const MethodComponent = React.memo((props: MethodProps) => { export const MethodComponent = React.memo((props: MethodProps) => {
const { name, parentPath, docString, addNotification } = props; const { name, parentPath, docString, addNotification } = props;
@ -89,12 +89,12 @@ export const MethodComponent = React.memo((props: MethodProps) => {
)} )}
<h5 onClick={() => setHideOutput(!hideOutput)} style={{ cursor: 'pointer' }}> <h5 onClick={() => setHideOutput(!hideOutput)} style={{ cursor: 'pointer' }}>
Function: {displayName} Function: {displayName}
<DocStringComponent docString={docString} />
</h5> </h5>
<Form onSubmit={execute}> <Form onSubmit={execute}>
{args} {args}
<Button variant="primary" type="submit"> <Button variant="primary" type="submit">
Execute Execute
<DocStringComponent docString={docString} />
</Button> </Button>
</Form> </Form>

View File

@ -32,7 +32,7 @@ export type FloatObject = {
}; };
export type NumberObject = IntObject | FloatObject | QuantityObject; export type NumberObject = IntObject | FloatObject | QuantityObject;
interface NumberComponentProps { type NumberComponentProps = {
name: string; name: string;
type: 'float' | 'int'; type: 'float' | 'int';
parentPath?: string; parentPath?: string;
@ -43,7 +43,7 @@ interface NumberComponentProps {
unit?: string; unit?: string;
showName?: boolean; showName?: boolean;
addNotification: (message: string, levelname?: LevelName) => void; addNotification: (message: string, levelname?: LevelName) => void;
} };
// TODO: highlight the digit that is being changed by setting both selectionStart and // TODO: highlight the digit that is being changed by setting both selectionStart and
// selectionEnd // selectionEnd
@ -313,10 +313,14 @@ export const NumberComponent = React.memo((props: NumberComponentProps) => {
{process.env.NODE_ENV === 'development' && ( {process.env.NODE_ENV === 'development' && (
<div>Render count: {renderCount.current}</div> <div>Render count: {renderCount.current}</div>
)} )}
<DocStringComponent docString={docString} />
<div className="d-flex"> <div className="d-flex">
<InputGroup> <InputGroup>
{showName && <InputGroup.Text>{displayName}</InputGroup.Text>} {showName && (
<InputGroup.Text>
{displayName}
<DocStringComponent docString={docString} />
</InputGroup.Text>
)}
<Form.Control <Form.Control
type="text" type="text"
value={inputString} value={inputString}

View File

@ -8,7 +8,7 @@ import { NumberComponent, NumberObject } from './NumberComponent';
import { getIdFromFullAccessPath } from '../utils/stringUtils'; import { getIdFromFullAccessPath } from '../utils/stringUtils';
import { LevelName } from './NotificationsComponent'; import { LevelName } from './NotificationsComponent';
interface SliderComponentProps { type SliderComponentProps = {
name: string; name: string;
min: NumberObject; min: NumberObject;
max: NumberObject; max: NumberObject;
@ -19,7 +19,7 @@ interface SliderComponentProps {
stepSize: NumberObject; stepSize: NumberObject;
isInstantUpdate: boolean; isInstantUpdate: boolean;
addNotification: (message: string, levelname?: LevelName) => void; addNotification: (message: string, levelname?: LevelName) => void;
} };
export const SliderComponent = React.memo((props: SliderComponentProps) => { export const SliderComponent = React.memo((props: SliderComponentProps) => {
const renderCount = useRef(0); const renderCount = useRef(0);
@ -105,10 +105,12 @@ export const SliderComponent = React.memo((props: SliderComponentProps) => {
<div>Render count: {renderCount.current}</div> <div>Render count: {renderCount.current}</div>
)} )}
<DocStringComponent docString={docString} />
<Row> <Row>
<Col xs="auto" xl="auto"> <Col xs="auto" xl="auto">
<InputGroup.Text>{displayName}</InputGroup.Text> <InputGroup.Text>
{displayName}
<DocStringComponent docString={docString} />
</InputGroup.Text>
</Col> </Col>
<Col xs="5" xl> <Col xs="5" xl>
<Slider <Slider

View File

@ -9,7 +9,7 @@ import { WebSettingsContext } from '../WebSettings';
// TODO: add button functionality // TODO: add button functionality
interface StringComponentProps { type StringComponentProps = {
name: string; name: string;
parentPath?: string; parentPath?: string;
value: string; value: string;
@ -17,7 +17,7 @@ interface StringComponentProps {
docString: string; docString: string;
isInstantUpdate: boolean; isInstantUpdate: boolean;
addNotification: (message: string, levelname?: LevelName) => void; addNotification: (message: string, levelname?: LevelName) => void;
} };
export const StringComponent = React.memo((props: StringComponentProps) => { export const StringComponent = React.memo((props: StringComponentProps) => {
const { name, parentPath, readOnly, docString, isInstantUpdate, addNotification } = const { name, parentPath, readOnly, docString, isInstantUpdate, addNotification } =
@ -70,9 +70,11 @@ export const StringComponent = React.memo((props: StringComponentProps) => {
{process.env.NODE_ENV === 'development' && ( {process.env.NODE_ENV === 'development' && (
<div>Render count: {renderCount.current}</div> <div>Render count: {renderCount.current}</div>
)} )}
<DocStringComponent docString={docString} />
<InputGroup> <InputGroup>
<InputGroup.Text>{displayName}</InputGroup.Text> <InputGroup.Text>
{displayName}
<DocStringComponent docString={docString} />
</InputGroup.Text>
<Form.Control <Form.Control
type="text" type="text"
value={inputString} value={inputString}

View File

@ -1,13 +1,13 @@
{ {
"files": { "files": {
"main.css": "/static/css/main.2d8458eb.css", "main.css": "/static/css/main.2d8458eb.css",
"main.js": "/static/js/main.ea55bba6.js", "main.js": "/static/js/main.dba067e7.js",
"index.html": "/index.html", "index.html": "/index.html",
"main.2d8458eb.css.map": "/static/css/main.2d8458eb.css.map", "main.2d8458eb.css.map": "/static/css/main.2d8458eb.css.map",
"main.ea55bba6.js.map": "/static/js/main.ea55bba6.js.map" "main.dba067e7.js.map": "/static/js/main.dba067e7.js.map"
}, },
"entrypoints": [ "entrypoints": [
"static/css/main.2d8458eb.css", "static/css/main.2d8458eb.css",
"static/js/main.ea55bba6.js" "static/js/main.dba067e7.js"
] ]
} }

View File

@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site displaying a pydase UI."/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>pydase App</title><script defer="defer" src="/static/js/main.ea55bba6.js"></script><link href="/static/css/main.2d8458eb.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html> <!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site displaying a pydase UI."/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>pydase App</title><script defer="defer" src="/static/js/main.dba067e7.js"></script><link href="/static/css/main.2d8458eb.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,6 @@
import inspect import inspect
import logging import logging
import sys
from collections.abc import Callable from collections.abc import Callable
from enum import Enum from enum import Enum
from typing import Any from typing import Any
@ -67,7 +68,9 @@ class Serializer:
def _serialize_enum(obj: Enum) -> dict[str, Any]: def _serialize_enum(obj: Enum) -> dict[str, Any]:
value = obj.name value = obj.name
readonly = False readonly = False
doc = get_attribute_doc(obj) doc = obj.__doc__
if sys.version_info < (3, 11) and doc == "An enumeration.":
doc = None
if type(obj).__base__.__name__ == "ColouredEnum": if type(obj).__base__.__name__ == "ColouredEnum":
obj_type = "ColouredEnum" obj_type = "ColouredEnum"
else: else:

View File

@ -100,6 +100,8 @@ def test_enum_serialize() -> None:
def test_ColouredEnum_serialize() -> None: def test_ColouredEnum_serialize() -> None:
class Status(ColouredEnum): class Status(ColouredEnum):
"""Status description."""
PENDING = "#FFA500" PENDING = "#FFA500"
RUNNING = "#0000FF80" RUNNING = "#0000FF80"
PAUSED = "rgb(169, 169, 169)" PAUSED = "rgb(169, 169, 169)"
@ -121,7 +123,7 @@ def test_ColouredEnum_serialize() -> None:
"RUNNING": "#0000FF80", "RUNNING": "#0000FF80",
}, },
"readonly": False, "readonly": False,
"doc": None, "doc": "Status description.",
} }