Skip to content

Commit 1d7a77a

Browse files
authored
experimental(highlight-data): Highlight data if it has a special qualifier (#491)
* experimental(highlight-data) * Add comments and docu
1 parent ea7f765 commit 1d7a77a

File tree

7 files changed

+101
-1
lines changed

7 files changed

+101
-1
lines changed

src/app/EnvProvider.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export const initialEnvValues: EnvironmentalVariables = {
3838
KEYCLOAK_REALM: undefined,
3939
KEYCLOAK_CLIENT_ID: undefined,
4040
SERIALIZATION_API_URL: undefined,
41+
EXPERIMENTAL_HIGHLIGHT_DATA_FLAG: false,
4142
};
4243

4344
const EnvContext = createContext(initialEnvValues);
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { Box } from '@mui/system';
2+
import { Qualifiable } from 'lib/api/aas/models';
3+
4+
/**
5+
* Retrieves highlighting information from a qualifiable object.
6+
*
7+
* @param qualifiable - The qualifiable object to extract highlighting information from
8+
* Qualifier needs to be of type "HighlightColor"
9+
* @returns An object containing:
10+
* - `highlight`: Boolean indicating whether highlighting should be applied
11+
* - `highlightColor`: The color value for highlighting, or empty string if no highlighting
12+
*
13+
* @remarks
14+
* Searches for a qualifier with type 'HighlightColor' in the qualifiable's qualifiers array.
15+
* If no qualifiers exist or no matching qualifier is found, returns default non-highlight values.
16+
*/
17+
const getHighlighting = (qualifiable: Qualifiable) => {
18+
const noHighlight = {
19+
highlight: false,
20+
highlightColor: '',
21+
};
22+
if (!qualifiable?.qualifiers) {
23+
return noHighlight;
24+
}
25+
26+
const qualifier = qualifiable.qualifiers.find((qualifiable) => {
27+
return qualifiable.type === 'HighlightColor';
28+
});
29+
30+
if (!qualifier || !qualifier.value) {
31+
return noHighlight;
32+
}
33+
return {
34+
highlight: true,
35+
highlightColor: qualifier.value,
36+
};
37+
};
38+
39+
/**
40+
* Renders a colored highlight box based on qualifier data.
41+
*
42+
* @param highlightData - Boolean flag to enable/disable highlight rendering
43+
* @param property - The qualifiable property containing potential highlight qualifier information
44+
* @returns A JSX element with a colored box if highlighting is enabled and valid, otherwise null
45+
*
46+
* @remarks
47+
* This is an experimental feature that displays a vertical colored bar (8px wide, 1.5em high)
48+
* when all conditions are met: highlightData is true, property is defined, and a valid
49+
* HighlightColor qualifier exists.
50+
*/
51+
export const renderHighlight = (highlightData: boolean, property: Qualifiable | undefined) => {
52+
const highlighting = property ? getHighlighting(property) : { highlight: false, highlightColor: '' };
53+
return (
54+
highlightData &&
55+
property &&
56+
highlighting.highlight && (
57+
// EXPERIMENTAL FEATURE
58+
<Box
59+
sx={{
60+
width: '8px',
61+
borderRadius: '2px',
62+
backgroundColor: highlighting.highlightColor,
63+
marginRight: '12px',
64+
height: '1.5em',
65+
flexShrink: 0,
66+
}}
67+
/>
68+
// ----
69+
)
70+
);
71+
};

src/app/[locale]/viewer/_components/submodel-elements/generic-elements/GenericPropertyComponent.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import { Box, IconButton, Link, Skeleton, Tooltip, Typography } from '@mui/material';
2-
import { MultiLanguageProperty, Property, Range, ConceptDescription } from 'lib/api/aas/models';
2+
import {
3+
MultiLanguageProperty,
4+
Property,
5+
Range,
6+
ConceptDescription,
7+
} from 'lib/api/aas/models';
38
import { getTranslationText } from 'lib/util/SubmodelResolverUtil';
49
import { isValidUrl } from 'lib/util/UrlUtil';
510
import { ContentCopy, OpenInNew } from '@mui/icons-material';
@@ -8,6 +13,8 @@ import { useNotificationSpawner } from 'lib/hooks/UseNotificationSpawner';
813
import { useLocale } from 'use-intl';
914
import { useTranslations } from 'next-intl';
1015
import { getUnitFromConceptDescription } from 'app/[locale]/viewer/_components/submodel/technical-data/ConceptDescriptionHelper';
16+
import { useEnv } from 'app/EnvProvider';
17+
import { renderHighlight } from '../../HighlightBoxExperimental';
1118

1219
type GenericPropertyComponentProps = {
1320
readonly property?: Property;
@@ -28,6 +35,8 @@ export function GenericPropertyComponent(props: GenericPropertyComponentProps) {
2835
const locale = useLocale();
2936
const [isHovered, setIsHovered] = useState(false);
3037
const notificationSpawner = useNotificationSpawner();
38+
const env = useEnv();
39+
const highlightData = env.EXPERIMENTAL_HIGHLIGHT_DATA_FLAG;
3140

3241
let value: string | null = null;
3342
if (mLangProp) {
@@ -115,6 +124,7 @@ export function GenericPropertyComponent(props: GenericPropertyComponentProps) {
115124
onMouseEnter={() => setIsHovered(true)}
116125
onMouseLeave={() => setIsHovered(false)}
117126
>
127+
{renderHighlight(highlightData, property)}
118128
<Typography data-testid="property-content">
119129
{value || t('labels.notAvailable')}
120130
<span> </span>

src/app/[locale]/viewer/_components/submodel-elements/generic-elements/MultiLanguagePropertyComponent.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import { useNotificationSpawner } from 'lib/hooks/UseNotificationSpawner';
77
import { useLocale } from 'use-intl';
88
import { useTranslations } from 'next-intl';
99
import { MultiLanguageProperty } from 'lib/api/aas/models';
10+
import { useEnv } from 'app/EnvProvider';
11+
import { renderHighlight } from '../../HighlightBoxExperimental';
1012

1113
type MultiLanguagePropertyComponentProps = {
1214
readonly mLangProp: MultiLanguageProperty;
@@ -19,6 +21,8 @@ export function MultiLanguagePropertyComponent(props: MultiLanguagePropertyCompo
1921
const value = getTranslationText(mLangProp, locale);
2022
const [isHovered, setIsHovered] = useState(false);
2123
const notificationSpawner = useNotificationSpawner();
24+
const env = useEnv();
25+
const highlightData = env.EXPERIMENTAL_HIGHLIGHT_DATA_FLAG;
2226

2327
const handleCopyValue = () => {
2428
if (value) {
@@ -72,6 +76,7 @@ export function MultiLanguagePropertyComponent(props: MultiLanguagePropertyCompo
7276
onMouseEnter={() => setIsHovered(true)}
7377
onMouseLeave={() => setIsHovered(false)}
7478
>
79+
{renderHighlight(highlightData, props.mLangProp)}
7580
<Typography data-testid="mlproperty-content">{value || '-'}</Typography>
7681
{renderCopyButton()}
7782
</Box>

src/components/basics/VerticalTabSelector.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { ReactElement, useState } from 'react';
44
import { Submodel } from 'lib/api/aas/models';
55
import { useTranslations } from 'next-intl';
66
import { useIsMobile } from 'lib/hooks/UseBreakpoints';
7+
import { useEnv } from 'app/EnvProvider';
8+
import { renderHighlight } from 'app/[locale]/viewer/_components/HighlightBoxExperimental';
79

810
export type TabSelectorItem = {
911
readonly id: string;
@@ -87,6 +89,8 @@ export function VerticalTabSelector(props: VerticalTabSelectorProps) {
8789
const [hoveredItem, setHoveredItem] = useState<TabSelectorItem>();
8890
const isMobile = useIsMobile();
8991
const t = useTranslations('components.verticalTabSelector.errors');
92+
const env = useEnv();
93+
const highlightData = env.EXPERIMENTAL_HIGHLIGHT_DATA_FLAG;
9094

9195
const selectedCSSClass = (id: string) => (id === props.selected?.id ? 'selected' : '');
9296

@@ -135,6 +139,8 @@ export function VerticalTabSelector(props: VerticalTabSelectorProps) {
135139
flex: 1,
136140
}}
137141
>
142+
{renderHighlight(highlightData, item.submodelData)}
143+
138144
<Typography
139145
sx={{
140146
overflow: 'hidden',

src/lib/env/MnestixEnv.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ const featureFlags = mapEnvVariables(
4949
'WHITELIST_FEATURE_FLAG',
5050
'KEYCLOAK_ENABLED',
5151
'BASYX_RBAC_ENABLED',
52+
'EXPERIMENTAL_HIGHLIGHT_DATA_FLAG'
5253
] as const,
5354
parseFlag,
5455
);

wiki/Mnestix-Configuration-Settings.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ Mnestix provides the following configuration options. You can adapt the values i
2828
| `WHITELIST_FEATURE_FLAG` | false | Enables or disables the feature for showing/hiding specific submodels. |
2929
| `SUBMODEL_WHITELIST` | | This variable can be used to specify a list of submodel semantic ids in order to show them when the `WHITELIST_FEATURE_FLAG` is set to true. |
3030

31+
#### Experimental Features
32+
33+
| Name | Default value | Description |
34+
| ---------------------------------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
35+
| `EXPERIMENTAL_HIGHLIGHT_DATA_FLAG` | false | Enables highlighting of specific submodels, properties, and multilang properties rendered with the default/fallback visualization. When enabled, elements can be visually emphasized using a `HighlightColor` qualifier. Example qualifier configuration: `{"kind": "ValueQualifier", "type": "HighlightColor", "value": "rgb(255,0,0)", "valueType": "xs:string"}`. For visual examples, see [PR #491](https://github.com/eclipse-mnestix/mnestix-browser/pull/491). |
36+
3137
#### Keycloak
3238

3339
See [Keycloak Configuration](Keycloak-Configuration) for more information about the Keycloak setup.

0 commit comments

Comments
 (0)