Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
12 changes: 12 additions & 0 deletions projects/ngx-coding-components/src/lib/translations/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,18 @@
"error-unselected-source": "Der Ausdruck verweist auf nicht ausgewählte Quelle(n)",
"error-invalid-value": "Bitte numerischen Testwert prüfen",
"error-evaluation": "Der Ausdruck konnte nicht ausgewertet werden. Bitte Syntax, Variablennamen und numerische Testwerte prüfen."
},
"solver-help": {
"title": "Syntaxhilfe für Solver-Ausdrücke",
"description": "Solver-Ausdrücke werden mit math.js ausgewertet. Verwenden Sie Zahlen, Rechenzeichen und math.js-Funktionen; Quellvariablen referenzieren Sie mit ${Variablenname} oder ${VariablenID}.",
"examples-title": "Beispiele:",
"examples": {
"arithmetic": "einfache Rechenoperationen",
"variables": "Werte aus zwei Quellvariablen addieren",
"function": "Prozentwert berechnen und runden",
"condition": "Bedingung: Ergebnis ist 1, wenn Punkte mindestens 5 sind, sonst 0"
},
"docs-link": "math.js-Syntax öffnen"
}
},
"processing": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,39 @@ <h1 mat-dialog-title>{{ (newVariableMode ? 'varList.add' : 'derive-processing.pr
}
}
@if (data.sourceType === 'SOLVER') {
<mat-form-field>
<mat-form-field class="solver-expression-field">
<mat-label>{{ 'derive-processing.SOLVER_EXPRESSION' | translate }}</mat-label>
<input matInput [(ngModel)]="data.sourceParameters.solverExpression"
(ngModelChange)="clearSolverTestResult()">
</mat-form-field>

<div class="solver-expression-help" role="note" aria-labelledby="solver-expression-help-title">
<div class="solver-expression-help__header">
<mat-icon class="solver-expression-help__icon" aria-hidden="true">info</mat-icon>
<span id="solver-expression-help-title">
{{ 'derive-processing.solver-help.title' | translate }}
</span>
</div>
<p>{{ 'derive-processing.solver-help.description' | translate }}</p>
<div>{{ 'derive-processing.solver-help.examples-title' | translate }}</div>
<ul>
@for (example of solverExpressionExamples; track example.expression) {
<li>
<code>{{ example.expression }}</code>
-
{{ example.descriptionKey | translate }}
</li>
}
</ul>
<a class="solver-expression-help__link"
[href]="solverExpressionDocsUrl"
target="_blank"
rel="noopener noreferrer">
{{ 'derive-processing.solver-help.docs-link' | translate }}
<mat-icon aria-hidden="true">open_in_new</mat-icon>
</a>
</div>

<section class="solver-test-area">
<div class="solver-test-header">
<div class="solver-test-title">{{ 'derive-processing.solver-test.title' | translate }}</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -238,4 +238,21 @@ describe('EditSourceParametersDialog', () => {
expect(dialog.solverTestValues).toEqual({ v2: '' });
expect(dialog.solverTestResult).toBeNull();
});

it('should provide solver expression help examples and docs link', () => {
const dialog = createDialog({ sourceType: 'SOLVER' });
const expressions = dialog.solverExpressionExamples.map(
example => example.expression
);

expect(dialog.solverExpressionDocsUrl).toBe(
'https://mathjs.org/docs/expressions/syntax.html'
);
expect(expressions).toContain('1 + 2 * 3');
expect(expressions.some(expression => expression.includes('${'))).toBeTrue();
expect(expressions.some(expression => expression.includes('round('))).toBeTrue();
expect(expressions.some(
expression => expression.includes('?') && expression.includes(':')
)).toBeTrue();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,123 @@ type SolverTestResult = {
message: string;
};

interface SolverExpressionExample {
expression: string;
descriptionKey: string;
}

const SOLVER_VARIABLE_PREFIX = '$';

@Component({
templateUrl: 'edit-source-parameters-dialog.component.html',
styles: [`
.solver-expression-field {
width: 100%;
}

.solver-expression-help {
background: #f5f7ff;
border-left: 4px solid #3f51b5;
border-radius: 4px;
color: rgb(0 0 0 / 82%);
margin: -8px 0 8px;
max-width: 640px;
padding: 12px 14px;
}

.solver-expression-help__header {
align-items: center;
display: flex;
gap: 8px;
font-weight: 500;
}

.solver-expression-help__icon {
color: #3f51b5;
font-size: 20px;
height: 20px;
width: 20px;
}

.solver-expression-help p {
margin: 8px 0;
}

.solver-expression-help ul {
margin: 6px 0 10px;
padding-left: 20px;
}

.solver-expression-help li {
margin-bottom: 4px;
}

.solver-expression-help code {
background: rgb(0 0 0 / 6%);
border-radius: 3px;
overflow-wrap: anywhere;
padding: 1px 4px;
white-space: normal;
}

.solver-expression-help__link {
align-items: center;
display: inline-flex;
gap: 4px;
}

.solver-expression-help__link mat-icon {
font-size: 16px;
height: 16px;
width: 16px;
}
`,
`
.solver-test-area {
border-top: 1px solid rgba(0, 0, 0, 0.12);
margin-top: 8px;
padding-top: 16px;
}

.solver-test-header {
align-items: center;
display: flex;
gap: 16px;
justify-content: space-between;
margin-bottom: 12px;
}

.solver-test-title {
font-weight: 600;
}

.solver-test-values {
display: grid;
gap: 8px 12px;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
}

.solver-test-result {
border-radius: 4px;
margin-top: 8px;
padding: 8px 12px;
}

.solver-test-result-ok {
background: #e8f5e9;
color: #1b5e20;
}

.solver-test-result-error {
background: #ffebee;
color: #b71c1c;
}

.solver-test-hint {
color: rgba(0, 0, 0, 0.6);
margin-bottom: 8px;
}
`],
standalone: true,
imports: [
MatSelectTrigger,
Expand All @@ -58,7 +173,6 @@ type SolverTestResult = {
MatButton,
MatDialogClose,
TranslateModule,
MatIcon,
FormsModule,
MatFormField,
MatInput,
Expand All @@ -69,58 +183,34 @@ type SolverTestResult = {
KeyValuePipe,
NgForOf,
ReactiveFormsModule,
NgIf
],
styles: [
`
.solver-test-area {
border-top: 1px solid rgba(0, 0, 0, 0.12);
margin-top: 8px;
padding-top: 16px;
}

.solver-test-header {
align-items: center;
display: flex;
gap: 16px;
justify-content: space-between;
margin-bottom: 12px;
}

.solver-test-title {
font-weight: 600;
}

.solver-test-values {
display: grid;
gap: 8px 12px;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
}

.solver-test-result {
border-radius: 4px;
margin-top: 8px;
padding: 8px 12px;
}

.solver-test-result-ok {
background: #e8f5e9;
color: #1b5e20;
}

.solver-test-result-error {
background: #ffebee;
color: #b71c1c;
}

.solver-test-hint {
color: rgba(0, 0, 0, 0.6);
margin-bottom: 8px;
}
`
NgIf,
MatIcon
]
})
export class EditSourceParametersDialog {
readonly solverExpressionDocsUrl =
'https://mathjs.org/docs/expressions/syntax.html';

readonly solverExpressionExamples: SolverExpressionExample[] = [
{
expression: '1 + 2 * 3',
descriptionKey: 'derive-processing.solver-help.examples.arithmetic'
},
{
expression: `${SOLVER_VARIABLE_PREFIX}{Punkte} + ${SOLVER_VARIABLE_PREFIX}{Bonus}`,
descriptionKey: 'derive-processing.solver-help.examples.variables'
},
{
expression: `round(${SOLVER_VARIABLE_PREFIX}{Punkte} / ` +
`${SOLVER_VARIABLE_PREFIX}{MaxPunkte} * 100, 1)`,
descriptionKey: 'derive-processing.solver-help.examples.function'
},
{
expression: `${SOLVER_VARIABLE_PREFIX}{Punkte} >= 5 ? 1 : 0`,
descriptionKey: 'derive-processing.solver-help.examples.condition'
}
];

sourceTypeList: SourceType[] = [
'COPY_VALUE',
'CONCAT_CODE',
Expand Down
33 changes: 32 additions & 1 deletion src/assets/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,38 @@
"derive-method": {
"label": "Ableitungsmethode",
"prompt": "Ableitungsmethode",
"BASE": "Basisvariable"
"BASE": "Basisvariable",
"COPY_VALUE": "Wert kopieren",
"CONCAT_CODE": "Codes aneinanderhängen",
"SUM_CODE": "Codes summieren",
"SUM_SCORE": "Bewertung summieren",
"UNIQUE_VALUES": "Auf Einzigartigkeit prüfen",
"SOLVER": "Mathematischer Ausdruck (Solver)",
"MANUAL": "Manuell"
},
"derive-processing": {
"prompt": "Basis-/Ableitungsparameter",
"TO_LOWER_CASE": "In Kleinbuchstaben umwandeln",
"TO_NUMBER": "In Zahl umwandeln",
"REMOVE_ALL_SPACES": "Alle Leerzeichen entfernen",
"REMOVE_DISPENSABLE_SPACES": "Überflüssige Leerzeichen entfernen",
"TAKE_DISPLAYED_AS_VALUE_CHANGED": "Angezeigt = Wert geändert",
"TAKE_NOT_REACHED_AS_VALUE_CHANGED": "Nicht erreicht = Wert geändert",
"TAKE_EMPTY_AS_VALID": "Leerer Antwortwert ist gültig",
"SORT": "Sortiere Werte",
"SOLVER_EXPRESSION": "Ausdruck für Solver",
"solver-help": {
"title": "Syntaxhilfe für Solver-Ausdrücke",
"description": "Solver-Ausdrücke werden mit math.js ausgewertet. Verwenden Sie Zahlen, Rechenzeichen und math.js-Funktionen; Quellvariablen referenzieren Sie mit ${Variablenname} oder ${VariablenID}.",
"examples-title": "Beispiele:",
"examples": {
"arithmetic": "einfache Rechenoperationen",
"variables": "Werte aus zwei Quellvariablen addieren",
"function": "Prozentwert berechnen und runden",
"condition": "Bedingung: Ergebnis ist 1, wenn Punkte mindestens 5 sind, sonst 0"
},
"docs-link": "math.js-Syntax öffnen"
}
},
"processing": {
"prompt": "Verarbeitung",
Expand Down
26 changes: 26 additions & 0 deletions src/assets/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,37 @@
"CONCAT_CODE": "Concatenate codes",
"SUM_CODE": "Sum codes",
"SUM_SCORE": "Sum scores",
"UNIQUE_VALUES": "Check uniqueness",
"SOLVER": "Mathematical expression (solver)",
"MANUAL": "Manual"
},
"derive-sources": {
"label": "Source variables",
"prompt": "Source variable(s)",
"error": "No source defined"
},
"derive-processing": {
"prompt": "Base/derivation parameters",
"TO_LOWER_CASE": "Convert to lower case",
"TO_NUMBER": "Convert to number",
"REMOVE_ALL_SPACES": "Remove all spaces",
"REMOVE_DISPENSABLE_SPACES": "Remove dispensable spaces",
"TAKE_DISPLAYED_AS_VALUE_CHANGED": "Displayed = value changed",
"TAKE_NOT_REACHED_AS_VALUE_CHANGED": "Not reached = value changed",
"TAKE_EMPTY_AS_VALID": "Empty response value is valid",
"SORT": "Sort values",
"SOLVER_EXPRESSION": "Expression for solver",
"solver-help": {
"title": "Syntax help for solver expressions",
"description": "Solver expressions are evaluated with math.js. Use numbers, operators and math.js functions; refer to source variables with ${VariableName} or ${VariableID}.",
"examples-title": "Examples:",
"examples": {
"arithmetic": "simple arithmetic",
"variables": "add values from two source variables",
"function": "calculate and round a percentage",
"condition": "condition: result is 1 when points are at least 5, otherwise 0"
},
"docs-link": "Open math.js syntax"
}
}
}
Loading