Skip to content

Commit

Permalink
feat: checkbox for inversion of visibility value
Browse files Browse the repository at this point in the history
* feat: implement visible entity
  • Loading branch information
FabienArcellier committed Aug 13, 2024
1 parent 72ab762 commit 564d35b
Show file tree
Hide file tree
Showing 12 changed files with 879 additions and 483 deletions.
610 changes: 521 additions & 89 deletions apps/hello/ui.json

Large diffs are not rendered by default.

23 changes: 14 additions & 9 deletions src/ui/src/builder/BuilderSettingsVisibility.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,55 +11,60 @@
:class="{
active:
typeof component.visible == 'undefined' ||
component.visible === true,
component.visible.expression === true,
}"
@click="() => setVisibleValue(component.id, true)"
>
Yes
</div>
<div
class="chip"
:class="{ active: component.visible === false }"
:class="{ active: component.visible?.expression === false }"
@click="() => setVisibleValue(component.id, false)"
>
No
</div>
<div
class="chip"
:class="{ active: typeof component.visible === 'string' }"
@click="() => setVisibleValue(component.id, '')"
:class="{ active: component.visible?.expression === 'custom' }"

Check failure on line 29 in src/ui/src/builder/BuilderSettingsVisibility.vue

View workflow job for this annotation

GitHub Actions / build (3.12)

Replace `·active:·component.visible?.expression·===·'custom'·` with `⏎↹↹↹↹↹↹active:·component.visible?.expression·===·'custom',⏎↹↹↹↹↹`

Check failure on line 29 in src/ui/src/builder/BuilderSettingsVisibility.vue

View workflow job for this annotation

GitHub Actions / build (3.10)

Replace `·active:·component.visible?.expression·===·'custom'·` with `⏎↹↹↹↹↹↹active:·component.visible?.expression·===·'custom',⏎↹↹↹↹↹`

Check failure on line 29 in src/ui/src/builder/BuilderSettingsVisibility.vue

View workflow job for this annotation

GitHub Actions / build (3.11)

Replace `·active:·component.visible?.expression·===·'custom'·` with `⏎↹↹↹↹↹↹active:·component.visible?.expression·===·'custom',⏎↹↹↹↹↹`

Check failure on line 29 in src/ui/src/builder/BuilderSettingsVisibility.vue

View workflow job for this annotation

GitHub Actions / build (3.10)

Replace `·active:·component.visible?.expression·===·'custom'·` with `⏎↹↹↹↹↹↹active:·component.visible?.expression·===·'custom',⏎↹↹↹↹↹`

Check failure on line 29 in src/ui/src/builder/BuilderSettingsVisibility.vue

View workflow job for this annotation

GitHub Actions / build (3.12)

Replace `·active:·component.visible?.expression·===·'custom'·` with `⏎↹↹↹↹↹↹active:·component.visible?.expression·===·'custom',⏎↹↹↹↹↹`

Check failure on line 29 in src/ui/src/builder/BuilderSettingsVisibility.vue

View workflow job for this annotation

GitHub Actions / build (3.9)

Replace `·active:·component.visible?.expression·===·'custom'·` with `⏎↹↹↹↹↹↹active:·component.visible?.expression·===·'custom',⏎↹↹↹↹↹`

Check failure on line 29 in src/ui/src/builder/BuilderSettingsVisibility.vue

View workflow job for this annotation

GitHub Actions / build (3.11)

Replace `·active:·component.visible?.expression·===·'custom'·` with `⏎↹↹↹↹↹↹active:·component.visible?.expression·===·'custom',⏎↹↹↹↹↹`

Check failure on line 29 in src/ui/src/builder/BuilderSettingsVisibility.vue

View workflow job for this annotation

GitHub Actions / build (3.12)

Replace `?active:?component.visible?.expression?===?'custom'?` with `????????active:?component.visible?.expression?===?'custom',???????`
@click="() => setVisibleValue(component.id, 'custom', component.visible.binding, component.visible.reversed)"

Check failure on line 30 in src/ui/src/builder/BuilderSettingsVisibility.vue

View workflow job for this annotation

GitHub Actions / build (3.12)

Replace `()·=>·setVisibleValue(component.id,·'custom',·component.visible.binding,·component.visible.reversed)` with `⏎↹↹↹↹↹↹()·=>⏎↹↹↹↹↹↹↹setVisibleValue(⏎↹↹↹↹↹↹↹↹component.id,⏎↹↹↹↹↹↹↹↹'custom',⏎↹↹↹↹↹↹↹↹component.visible.binding,⏎↹↹↹↹↹↹↹↹component.visible.reversed,⏎↹↹↹↹↹↹↹)⏎↹↹↹↹↹`

Check failure on line 30 in src/ui/src/builder/BuilderSettingsVisibility.vue

View workflow job for this annotation

GitHub Actions / build (3.10)

Replace `()·=>·setVisibleValue(component.id,·'custom',·component.visible.binding,·component.visible.reversed)` with `⏎↹↹↹↹↹↹()·=>⏎↹↹↹↹↹↹↹setVisibleValue(⏎↹↹↹↹↹↹↹↹component.id,⏎↹↹↹↹↹↹↹↹'custom',⏎↹↹↹↹↹↹↹↹component.visible.binding,⏎↹↹↹↹↹↹↹↹component.visible.reversed,⏎↹↹↹↹↹↹↹)⏎↹↹↹↹↹`

Check failure on line 30 in src/ui/src/builder/BuilderSettingsVisibility.vue

View workflow job for this annotation

GitHub Actions / build (3.11)

Replace `()·=>·setVisibleValue(component.id,·'custom',·component.visible.binding,·component.visible.reversed)` with `⏎↹↹↹↹↹↹()·=>⏎↹↹↹↹↹↹↹setVisibleValue(⏎↹↹↹↹↹↹↹↹component.id,⏎↹↹↹↹↹↹↹↹'custom',⏎↹↹↹↹↹↹↹↹component.visible.binding,⏎↹↹↹↹↹↹↹↹component.visible.reversed,⏎↹↹↹↹↹↹↹)⏎↹↹↹↹↹`

Check failure on line 30 in src/ui/src/builder/BuilderSettingsVisibility.vue

View workflow job for this annotation

GitHub Actions / build (3.10)

Replace `()·=>·setVisibleValue(component.id,·'custom',·component.visible.binding,·component.visible.reversed)` with `⏎↹↹↹↹↹↹()·=>⏎↹↹↹↹↹↹↹setVisibleValue(⏎↹↹↹↹↹↹↹↹component.id,⏎↹↹↹↹↹↹↹↹'custom',⏎↹↹↹↹↹↹↹↹component.visible.binding,⏎↹↹↹↹↹↹↹↹component.visible.reversed,⏎↹↹↹↹↹↹↹)⏎↹↹↹↹↹`

Check failure on line 30 in src/ui/src/builder/BuilderSettingsVisibility.vue

View workflow job for this annotation

GitHub Actions / build (3.12)

Replace `()·=>·setVisibleValue(component.id,·'custom',·component.visible.binding,·component.visible.reversed)` with `⏎↹↹↹↹↹↹()·=>⏎↹↹↹↹↹↹↹setVisibleValue(⏎↹↹↹↹↹↹↹↹component.id,⏎↹↹↹↹↹↹↹↹'custom',⏎↹↹↹↹↹↹↹↹component.visible.binding,⏎↹↹↹↹↹↹↹↹component.visible.reversed,⏎↹↹↹↹↹↹↹)⏎↹↹↹↹↹`

Check failure on line 30 in src/ui/src/builder/BuilderSettingsVisibility.vue

View workflow job for this annotation

GitHub Actions / build (3.9)

Replace `()·=>·setVisibleValue(component.id,·'custom',·component.visible.binding,·component.visible.reversed)` with `⏎↹↹↹↹↹↹()·=>⏎↹↹↹↹↹↹↹setVisibleValue(⏎↹↹↹↹↹↹↹↹component.id,⏎↹↹↹↹↹↹↹↹'custom',⏎↹↹↹↹↹↹↹↹component.visible.binding,⏎↹↹↹↹↹↹↹↹component.visible.reversed,⏎↹↹↹↹↹↹↹)⏎↹↹↹↹↹`

Check failure on line 30 in src/ui/src/builder/BuilderSettingsVisibility.vue

View workflow job for this annotation

GitHub Actions / build (3.11)

Replace `()·=>·setVisibleValue(component.id,·'custom',·component.visible.binding,·component.visible.reversed)` with `⏎↹↹↹↹↹↹()·=>⏎↹↹↹↹↹↹↹setVisibleValue(⏎↹↹↹↹↹↹↹↹component.id,⏎↹↹↹↹↹↹↹↹'custom',⏎↹↹↹↹↹↹↹↹component.visible.binding,⏎↹↹↹↹↹↹↹↹component.visible.reversed,⏎↹↹↹↹↹↹↹)⏎↹↹↹↹↹`

Check failure on line 30 in src/ui/src/builder/BuilderSettingsVisibility.vue

View workflow job for this annotation

GitHub Actions / build (3.12)

Replace `()?=>?setVisibleValue(component.id,?'custom',?component.visible.binding,?component.visible.reversed)` with `????????()?=>?????????setVisibleValue(??????????component.id,??????????'custom',??????????component.visible.binding,??????????component.visible.reversed,?????????)???????`
>
Custom
</div>
</div>
<div
v-if="typeof component.visible === 'string'"
v-if="
typeof component.visible != 'undefined' &&
component.visible.expression === 'custom'
"
class="fieldWrapper"
>
<span class="name">Visibility value</span>
<BuilderTemplateInput
:value="component.visible"
:value="component.visible.binding"
type="state"
class="content"
placeholder="my_visibility_state_value"
@input="
(ev: Event) =>
setVisibleValue(
component.id,
'custom',
(ev.target as HTMLInputElement).value,
component.visibleReversed,
component.visible.reversed,
)
"
/>
<div class="flexRow">
<input
type="checkbox"
:checked="component.visibleReversed"
:checked="component.visible.reversed"
@input="
(ev: Event) =>
setVisibleValue(
component.id,
component.visible,
'custom',
component.visible.binding,
(ev.target as HTMLInputElement).checked,
)
"
Expand Down
28 changes: 19 additions & 9 deletions src/ui/src/builder/useComponentActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,7 @@ export function useComponentActions(wf: Core, ssbm: BuilderManager) {
parentId,
content: initContent,
handlers: {},
position: position ?? getNextInsertionPosition(parentId, type),
visible: true,
position: position ?? getNextInsertionPosition(parentId, type)
};

return component;
Expand Down Expand Up @@ -694,7 +693,8 @@ export function useComponentActions(wf: Core, ssbm: BuilderManager) {
*/
function setVisibleValue(
componentId: Component["id"],
visible: Component["visible"],
visible: boolean | string,
binding: string = "",
reversed: boolean = false,
) {
const component = wf.getComponentById(componentId);
Expand All @@ -703,12 +703,22 @@ export function useComponentActions(wf: Core, ssbm: BuilderManager) {
ssbm.openMutationTransaction(transactionId, `Change visibility`, true);
ssbm.registerPreMutation(component);

if (visible === true && typeof component.visible != "undefined") {
delete component.visible;
delete component.visibleReversed;
} else {
component.visible = visible;
component.visibleReversed = reversed;
if (component.visible == null) {
component.visible = {
expression: true,
binding: "",
reversed: false,
};
}

if (typeof visible == "boolean") {
component.visible.expression = visible;
} else if (visible == "custom") {
component.visible = {
expression: "custom",
binding: binding,
reversed: reversed,
};
}

ssbm.registerPostMutation(component);
Expand Down
8 changes: 4 additions & 4 deletions src/ui/src/renderer/useEvaluator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,14 +209,14 @@ export function useEvaluator(wf: Core) {
if (!component) return;

if (typeof component.visible === "undefined") return true;
if (component.visible === true) return true;
if (component.visible === false) return false;
if (component.visible.expression === true) return true;
if (component.visible.expression === false) return false;
const evaluated = evaluateExpression(
component.visible as string,
component.visible.binding as string,
instancePath,
);

return component.visibleReversed === true ? !evaluated : !!evaluated;
return component.visible.reversed === true ? !evaluated : !!evaluated;
}

return {
Expand Down
9 changes: 7 additions & 2 deletions src/ui/src/writerTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ type ComponentId = string
* Multiple instances of a single component can exists. For example, via Repeater.
*/

type VisibleField = {
expression: boolean | string; // True | False | 'custom'
binding: string; // variable binding when expression is custom
reversed: boolean;
};

export type Component = {
id: ComponentId;
parentId: string;
Expand All @@ -18,8 +24,7 @@ export type Component = {
content: Record<string, string>;
isCodeManaged?: boolean;
handlers?: Record<string, string>;
visible?: boolean | string;
visibleReversed?: boolean;
visible?: VisibleField;
binding?: {
eventType: string;
stateRef: string;
Expand Down
7 changes: 4 additions & 3 deletions src/writer/app_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from pydantic import ValidationError
from watchdog.observers.polling import PollingObserver

from writer import VERSION
from writer import VERSION, audit_and_fix
from writer.core import EventHandlerRegistry, MiddlewareRegistry, WriterSession
from writer.core_ui import ingest_bmc_component_tree
from writer.ss_types import (
Expand Down Expand Up @@ -551,7 +551,7 @@ def run(self) -> None:
message = self.log_queue.get()
if message is None:
break
self.logger.handle(message)
self.logger.handle(message)


class AppRunner:
Expand Down Expand Up @@ -691,6 +691,8 @@ def _load_persisted_components(self) -> Dict:
components = file_payload.get("components")
if components is None:
raise ValueError("Components not found in file.")

components = audit_and_fix.fix_components(components)
return components

async def check_session(self, session_id: str) -> bool:
Expand Down Expand Up @@ -864,4 +866,3 @@ def run_async_in_thread():
thread.start()
thread.join()
return

46 changes: 46 additions & 0 deletions src/writer/audit_and_fix.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
"""
This module allows you to migrate obsolete data structures to newer data structures.
For example, when loading the ui.
"""


def fix_components(components: dict) -> dict:
"""
Migrates obsolete components to their newer format and avoids errors in Pydantic.
"""
components = _fix_visible_fields(components)
return components


def _fix_visible_fields(components: dict) -> dict:
"""
Migrates the component visibility attribute to a more descriptive format.
>>> components['root']['visible'] = True
>>> components['root']['visible'] = {
>>> 'expression': True,
>>> 'binding': "",
>>> 'reversed': False
>>> }
If the visible attribute does not exist, it remains absent.
"""
for key, value in components.items():
visible = value.get('visible')

if visible is not None and isinstance(visible, bool):
components[key]['visible'] = {
'expression': visible,
'binding': "",
"reversed": False
}

if visible is not None and isinstance(visible, str):
components[key]['visible'] = {
'expression': "custom",
'binding': visible,
'reversed': False
}

return components
11 changes: 8 additions & 3 deletions src/writer/core_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
import uuid
from contextvars import ContextVar
from enum import Enum
from typing import Any, Dict, List, Optional, Union, cast
from typing import Any, Dict, List, Literal, Optional, Union, cast

from pydantic import BaseModel, Field
from typing_extensions import TypedDict

current_parent_container: ContextVar[Union["Component", None]] = \
ContextVar("current_parent_container")
Expand Down Expand Up @@ -35,6 +36,11 @@ class Branch(Enum):
session_cmc = "session_cmc"


class VisibileFields(TypedDict):
expression: Union[bool, Literal['custom']]
binding: str
reversed: bool

class Component(BaseModel):
id: str = Field(default_factory=generate_component_id)
type: str
Expand All @@ -43,8 +49,7 @@ class Component(BaseModel):
position: int = 0
parentId: Optional[str] = None
handlers: Optional[Dict[str, str]] = None
visible: Optional[Union[bool, str]] = None
visibleReversed: Optional[Union[bool]] = None
visible: Optional[VisibileFields] = None
binding: Optional[Dict] = None

def to_dict(self) -> Dict:
Expand Down
13 changes: 13 additions & 0 deletions tests/backend/fixtures/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import io
import os.path

fixture_path = os.path.realpath(os.path.dirname(__file__))

def load_fixture_content(path) -> str:
"""
Load the contents of a file from the fixture folder
>>> c = load_fixture_content('obsoletes/ui_obsolete_visible.json')
"""
with io.open(os.path.join(fixture_path, path), 'r', encoding='utf-8') as f:
return f.read()
36 changes: 36 additions & 0 deletions tests/backend/fixtures/obsoletes/ui_obsolete_visible.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"root": {
"id": "root",
"type": "root",
"content": {
"appName": "Hello",
"emptinessColor": "#ffffff"
},
"isCodeManaged": false,
"position": 0
},
"bb4d0e86-619e-4367-a180-be28ab6059f4": {
"id": "root",
"type": "page",
"content": {
"pageMode": "",
"key": "main"
},
"isCodeManaged": false,
"position": 0,
"parentId": "root",
"visible": true
},
"bb4d0e86-619e-4367-a180-be28abxxxx": {
"id": "root",
"type": "page",
"content": {
"pageMode": "",
"key": "main"
},
"isCodeManaged": false,
"position": 0,
"parentId": "root",
"visible": "value"
}
}
27 changes: 27 additions & 0 deletions tests/backend/test_audit_and_fix.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import json

from writer import audit_and_fix

from backend.fixtures import load_fixture_content


def test_fix_components_should_fix_visible_fields():
# Given
obsolete_components = load_fixture_content('obsoletes/ui_obsolete_visible.json')
components = json.loads(obsolete_components)

# When
final_components = audit_and_fix.fix_components(components)

# Then
assert "visible" not in final_components["root"]
assert final_components['bb4d0e86-619e-4367-a180-be28ab6059f4']['visible'] == {
'expression': True,
'binding': "",
'reversed': False
}
assert final_components['bb4d0e86-619e-4367-a180-be28abxxxx']['visible'] == {
'expression': "custom",
'binding': "value",
'reversed': False
}
Loading

0 comments on commit 564d35b

Please sign in to comment.