Skip to content

Commit

Permalink
Refactor expressions (#12406)
Browse files Browse the repository at this point in the history
  • Loading branch information
TomasEng authored Mar 1, 2024
1 parent 1d68f1b commit 57e33d0
Show file tree
Hide file tree
Showing 181 changed files with 5,597 additions and 3,727 deletions.
89 changes: 75 additions & 14 deletions frontend/language/src/nb.json
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,62 @@
"deploy_to_test.shared_with_org_true": "Du har delt dine endringer med din organisasjon",
"deploy_to_test.write_permission_checking": "Sjekker om du har tilgang til å legge ut appen",
"deploy_to_test.write_permission_false": "Du har ikke tilgang til å legge ut appen",
"expression": "Uttrykk",
"expression.addSubexpression": "Legg til underuttrykk",
"expression.and": "og",
"expression.andOr": "og/eller",
"expression.cannotSaveSinceInvalid": "Uttrykket er ikke gyldig og kan derfor ikke lagres.",
"expression.cannotSimplify": "Uttrykket er ikke i et format som støttes av det forenklede verkøyet. Rediger uttrykket manuelt i stedet, eller fjern det for å starte på nytt.",
"expression.changeToSimplifiedWarning": "Uttrykket du jobber med er ikke gyldig og vil ikke bli lagret hvis du bytter fane. Er du sikker på at du fortsette?",
"expression.componentId": "Komponent-ID",
"expression.confirmDeleteSubexpression": "Er du sikker på at du vil slette dette underuttrykket?",
"expression.datamodelPath": "Datamodellfelt",
"expression.disabledLogicalOperator": "Det må være minst to underuttrykk i listen før en logisk operator kan legges til.",
"expression.error.invalidComponentId": "Det finnes ingen komponent med denne ID-en. Velg en fra listen.",
"expression.error.invalidDatamodelPath": "Det finnes intet datamodellfelt med denne pekeren. Velg en peker fra listen.",
"expression.error.invalidFirstOperand": "Den første verdien er ikke gyldig.",
"expression.error.invalidSecondOperand": "Den andre verdien er ikke gyldig.",
"expression.error.numericRelationOperatorWithWrongType": "Den valgte operatoren kan ikke brukes sammen med de valgte verdiene. Den kan kun brukes med tall.",
"expression.errorListFooter": "Rett opp feilene og prøv igjen.",
"expression.errorListHeader": "Uttrykket kan ikke lagres på grunn av følgende feil:",
"expression.false": "Usann",
"expression.firstOperand": "Første operand",
"expression.instanceContext.appId": "App-ID",
"expression.instanceContext.instanceId": "Instans-ID",
"expression.instanceContext.instanceOwnerPartyId": "Instanseier-ID",
"expression.instanceContextKey": "Nøkkel",
"expression.invalidExpression": "Uttrykket er ikke gyldig og kan derfor ikke redigeres i Studio.",
"expression.logicalOperation": "Logisk uttrykk",
"expression.logicalOperator": "Operator:",
"expression.logicalTupleOperator.and": "Og",
"expression.logicalTupleOperator.or": "Eller",
"expression.manual": "Manuell",
"expression.or": "eller",
"expression.readonlyComponentId": "Komponent-ID:",
"expression.readonlyDatamodelPath": "Datamodellfelt:",
"expression.readonlyInstanceContext": "Instanskontekst:",
"expression.relationalOperator": "Operator",
"expression.relationalOperator.equals": "er lik",
"expression.relationalOperator.greaterThan": "er større enn",
"expression.relationalOperator.greaterThanEq": "er større enn eller lik",
"expression.relationalOperator.lessThan": "er mindre enn",
"expression.relationalOperator.lessThanEq": "er mindre enn eller lik",
"expression.relationalOperator.notEquals": "er ikke lik",
"expression.saveAndClose": "Lagre og lukk",
"expression.secondOperand": "Andre operand",
"expression.simplified": "Forenklet",
"expression.subexpression": "Underuttrykk nummer {{number}}",
"expression.transformToLogical": "Gjør om til logisk uttrykk",
"expression.true": "Sann",
"expression.value": "Verdi",
"expression.valueType": "Type",
"expression.valueType.boolean": "Sann/usann",
"expression.valueType.component": "Komponent",
"expression.valueType.datamodel": "Datamodell",
"expression.valueType.instanceContext": "Instanskontekst",
"expression.valueType.null": "Ikke satt",
"expression.valueType.number": "Tall",
"expression.valueType.string": "Tekst",
"form_filler.error_report_header": "Det er et problem",
"form_filler.error_required": "Feltet er påkrevd",
"form_filler.file_upload_valid_file_format_all": "alle",
Expand Down Expand Up @@ -1020,7 +1076,8 @@
"right_menu.expressions_data_source_select": "Velg …",
"right_menu.expressions_data_source_string": "Streng",
"right_menu.expressions_data_source_value": "Dataverdi",
"right_menu.expressions_expressions_limit_reached_alert": "Du har nådd grensen for antall regler",
"right_menu.expressions_delete_confirm": "Er du sikker på at du vil slette dette uttrykket?",
"right_menu.expressions_expressions_limit_reached_alert": "Komponenten har ikke flere egenskaper som Studio kan sette regler på.",
"right_menu.expressions_function": "FunksjonForSubUttrykk",
"right_menu.expressions_function_equals": "er lik",
"right_menu.expressions_function_greater_than": "er større enn",
Expand All @@ -1031,26 +1088,30 @@
"right_menu.expressions_function_not_equals": "ikke er lik",
"right_menu.expressions_function_on_property": "Velg et oppsett",
"right_menu.expressions_function_select": "Velg oppsett …",
"right_menu.expressions_group_property_preview_show_add_button": "Vis Legg-til-knapp for <bold>{{componentName}}</bold> dersom",
"right_menu.expressions_group_property_preview_show_delete_button": "Vis Slett-knapp for <bold>{{componentName}}</bold> dersom",
"right_menu.expressions_group_property_preview_show_edit_button": "Vis Rediger-knapp for <bold>{{componentName}}</bold> dersom",
"right_menu.expressions_group_property_preview_show_save_button": "Vis Lagre-knapp for <bold>{{componentName}}</bold> dersom",
"right_menu.expressions_group_property_show_add_button": "Vis Legg-til-knapp dersom",
"right_menu.expressions_group_property_show_delete_button": "Vis Slett-knapp dersom",
"right_menu.expressions_group_property_show_edit_button": "Vis Rediger-knapp dersom",
"right_menu.expressions_group_property_show_save_button": "Vis Lagre-knapp dersom",
"right_menu.expressions_group_property_alert_on_delete": "Varsle ved sletting dersom …",
"right_menu.expressions_group_property_preview_alert_on_delete": "Varsle ved sletting dersom …",
"right_menu.expressions_group_property_preview_show_add_button": "Vis \"legg til\"-knapp for <bold>{{componentName}}</bold> dersom …",
"right_menu.expressions_group_property_preview_show_delete_button": "Vis \"slett\"-knapp for <bold>{{componentName}}</bold> dersom …",
"right_menu.expressions_group_property_preview_show_edit_button": "Vis \"rediger\"-knapp for <bold>{{componentName}}</bold> dersom …",
"right_menu.expressions_group_property_preview_show_save_and_next_button": "Vis \"lagre og neste\"-knapp for <bold>{{componentName}}</bold> dersom …",
"right_menu.expressions_group_property_preview_show_save_button": "Vis \"lagre\"-knapp for <bold>{{componentName}}</bold> dersom …",
"right_menu.expressions_group_property_show_add_button": "Vis \"legg til\"-knapp dersom …",
"right_menu.expressions_group_property_show_delete_button": "Vis \"slett\"-knapp dersom …",
"right_menu.expressions_group_property_show_edit_button": "Vis \"rediger\"-knapp dersom …",
"right_menu.expressions_group_property_show_save_and_next_button": "Vis \"lagre og neste\"-knapp dersom …",
"right_menu.expressions_group_property_show_save_button": "Vis \"lagre\"-knapp dersom …",
"right_menu.expressions_operator_and": "Og",
"right_menu.expressions_operator_or": "Eller",
"right_menu.expressions_property": "Komponentfelt med uttrykk",
"right_menu.expressions_property_hidden": "Skjul felt dersom",
"right_menu.expressions_property_on_component": "Velg hva som skal skje med <bold>{{componentName}}</bold>",
"right_menu.expressions_property_preview_hidden": "Skjul <bold>{{componentName}}</bold> hvis",
"right_menu.expressions_property_preview_read_only": "Sett <bold>{{componentName}}</bold> som read only hvis",
"right_menu.expressions_property_preview_required": "Sett <bold>{{componentName}}</bold> som påkrevd hvis",
"right_menu.expressions_property_read_only": "Sett felt som read only dersom",
"right_menu.expressions_property_preview_hidden": "Skjul <bold>{{componentName}}</bold> hvis",
"right_menu.expressions_property_preview_read_only": "Sett <bold>{{componentName}}</bold> som skrivebeskyttet hvis",
"right_menu.expressions_property_preview_required": "Sett <bold>{{componentName}}</bold> som påkrevd hvis",
"right_menu.expressions_property_read_only": "Sett felt som skrivebeskyttet dersom",
"right_menu.expressions_property_required": "Sett felt som påkrevd dersom",
"right_menu.expressions_property_select": "Velg regel …",
"right_menu.read_more_about_expressions": "<0 href=\"{{expressionDocs}}\" >Les mer om dynamiske uttrykk her.</0>",
"right_menu.read_more_about_expressions": "Les mer om dynamiske uttrykk i dokumentasjonen.",
"right_menu.rules_calculations": "Regel for beregninger",
"right_menu.rules_calculations_add_alt": "Legg til regel for beregninger",
"right_menu.rules_conditional_rendering": "Regel for vis/skjul felt",
Expand Down
14 changes: 0 additions & 14 deletions frontend/libs/studio-components/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,5 @@
module.exports = {
overrides: [
{
files: ['*.tsx'],
rules: {
'@typescript-eslint/naming-convention': [
'error',
{
selector: ['variable', 'function'],
modifiers: ['exported'],
format: ['PascalCase'],
prefix: ['Studio', 'use'],
},
],
},
},
{
files: ['*.tsx', '*.ts'],
rules: {
Expand Down
3 changes: 2 additions & 1 deletion frontend/libs/studio-components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
"@studio/icons": "^0.1.0",
"ajv": "8.12.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react-dom": "^18.2.0",
"uuid": "9.0.1"
},
"devDependencies": {
"@testing-library/jest-dom": "^6.1.3",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import type { RefObject } from 'react';
import React, { createRef } from 'react';
import { act, render, screen, within } from '@testing-library/react';
import type { StudioBooleanToggleGroupProps } from './StudioBooleanToggleGroup';
import { StudioBooleanToggleGroup } from './StudioBooleanToggleGroup';
import userEvent from '@testing-library/user-event';

describe('StudioBooleanToggleGroup', () => {
it('Renders a toggle group with toggles with the given labels', () => {
renderBooleanToggle();
const withinRadioGroup = () => within(screen.getByRole('radiogroup'));
expect(withinRadioGroup().getByRole('radio', { name: trueLabel })).toBeInTheDocument();
expect(withinRadioGroup().getByRole('radio', { name: falseLabel })).toBeInTheDocument();
});

it('Renders with the false toggle checked as default', () => {
renderBooleanToggle();
expect(getFalseToggle()).toBeChecked();
expect(getTrueToggle()).not.toBeChecked();
});

it('Renders with the true toggle checked when value is true', () => {
renderBooleanToggle({ value: true });
expect(getTrueToggle()).toBeChecked();
expect(getFalseToggle()).not.toBeChecked();
});

it('Renders with the false toggle checked when value is false', () => {
renderBooleanToggle({ value: false });
expect(getFalseToggle()).toBeChecked();
expect(getTrueToggle()).not.toBeChecked();
});

it('Calls the onChange callback with true when the user checks the true toggle', async () => {
const user = userEvent.setup();
const onChange = jest.fn();
renderBooleanToggle({ onChange, value: false });
await act(() => user.click(getTrueToggle()));
expect(onChange).toHaveBeenCalledTimes(1);
expect(onChange).toHaveBeenCalledWith(true);
});

it('Switches the toggle when the user checks the true toggle', async () => {
const user = userEvent.setup();
renderBooleanToggle({ value: false });
await act(() => user.click(getTrueToggle()));
expect(getTrueToggle()).toBeChecked();
expect(getFalseToggle()).not.toBeChecked();
});

it('Calls the onChange callback with false when the user checks the false toggle', async () => {
const user = userEvent.setup();
const onChange = jest.fn();
renderBooleanToggle({ onChange, value: true });
await act(() => user.click(getFalseToggle()));
expect(onChange).toHaveBeenCalledTimes(1);
expect(onChange).toHaveBeenCalledWith(false);
});

it('Switches the toggle when the user checks the false toggle', async () => {
const user = userEvent.setup();
renderBooleanToggle({ value: true });
await act(() => user.click(getFalseToggle()));
expect(getFalseToggle()).toBeChecked();
expect(getTrueToggle()).not.toBeChecked();
});

it('Updates the value when the value prop changes', () => {
const { rerender } = renderBooleanToggle({ value: true });
expect(getTrueToggle()).toBeChecked();
rerender(<StudioBooleanToggleGroup {...defaultProps} value={false} />);
expect(getFalseToggle()).toBeChecked();
rerender(<StudioBooleanToggleGroup {...defaultProps} value={true} />);
expect(getTrueToggle()).toBeChecked();
});

it('Forwards the ref object to the toggle group element if given', () => {
const ref = createRef<HTMLDivElement>();
const { container } = renderBooleanToggle({}, ref);
expect(ref.current).toBe(container.firstChild); // eslint-disable-line testing-library/no-node-access
});

const getTrueToggle = () => screen.getByRole('radio', { name: trueLabel });
const getFalseToggle = () => screen.getByRole('radio', { name: falseLabel });
});

const trueLabel = 'True';
const falseLabel = 'False';
const defaultProps: StudioBooleanToggleGroupProps = {
trueLabel,
falseLabel,
};

const renderBooleanToggle = (
props: Partial<StudioBooleanToggleGroupProps> = {},
ref?: RefObject<HTMLDivElement>,
) => render(<StudioBooleanToggleGroup {...defaultProps} {...props} ref={ref} />);
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import type { ToggleGroupProps } from '@digdir/design-system-react';
import { ToggleGroup } from '@digdir/design-system-react';
import React, { forwardRef, useEffect, useState } from 'react';

export type StudioBooleanToggleGroupProps = {
onChange?: (value: boolean) => void;
value?: boolean;
trueLabel: string;
falseLabel: string;
} & Omit<ToggleGroupProps, 'onChange' | 'value'>;

const StudioBooleanToggleGroup = forwardRef<HTMLDivElement, StudioBooleanToggleGroupProps>(
({ falseLabel, onChange, trueLabel, value: givenValue, ...rest }, ref) => {
const [value, setValue] = useState<boolean>(givenValue ?? false);

useEffect(() => {
setValue(givenValue ?? false);
}, [givenValue]);

const handleChange = (stringValue: 'true' | 'false') => {
const newValue = stringValue === 'true';
setValue(newValue);
onChange?.(newValue);
};

return (
<ToggleGroup {...rest} onChange={handleChange} value={value ? 'true' : 'false'} ref={ref}>
<ToggleGroup.Item value='true'>{trueLabel}</ToggleGroup.Item>
<ToggleGroup.Item value='false'>{falseLabel}</ToggleGroup.Item>
</ToggleGroup>
);
},
);

StudioBooleanToggleGroup.displayName = 'StudioBooleanToggleGroup';

export { StudioBooleanToggleGroup };
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { StudioBooleanToggleGroup } from './StudioBooleanToggleGroup';
export type { StudioBooleanToggleGroupProps } from './StudioBooleanToggleGroup';
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.code {
background-color: var(--fds-semantic-surface-neutral-subtle);
border: 1px solid var(--fds-semantic-border-neutral-subtle);
border-radius: var(--fds-border_radius-small);
font-family: 'Courier New', monospace;
font-size: 0.8em;
padding: 0 var(--fds-spacing-1);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React, { createRef } from 'react';
import { render, screen } from '@testing-library/react';
import { StudioCodeFragment } from './StudioCodeFragment';

jest.mock('./StudioCodeFragment.module.css', () => ({
code: 'code',
}));

/* eslint-disable testing-library/no-node-access */
describe('StudioCodeFragment', () => {
it('Renders the given content', () => {
const content = 'Test';
render(<StudioCodeFragment>{content}</StudioCodeFragment>);
expect(screen.getByText(content)).toBeInTheDocument();
});

it('Appends given classname to internal classname', () => {
const className = 'test-class';
const { container } = render(<StudioCodeFragment className={className} />);
expect(container.firstChild).toHaveClass(className);
expect(container.firstChild).toHaveClass('code');
});

it('Adds any additonal props to the element', () => {
const dataTestId = 'test';
render(<StudioCodeFragment data-testid={dataTestId} />);
expect(screen.getByTestId(dataTestId)).toBeInTheDocument();
});

it('Forwards the ref object to the code element if given', () => {
const ref = createRef<HTMLElement>();
const { container } = render(<StudioCodeFragment ref={ref} />);
expect(ref.current).toBe(container.firstChild);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { HTMLAttributes } from 'react';
import React, { forwardRef } from 'react';
import cn from 'classnames';
import classes from './StudioCodeFragment.module.css';

export type StudioCodeFragmentProps = HTMLAttributes<HTMLElement>;

const StudioCodeFragment = forwardRef<HTMLElement, StudioCodeFragmentProps>(
({ children, className: givenClass, ...rest }, ref) => {
const className = cn(classes.code, givenClass);
return (
<code className={className} {...rest} ref={ref}>
{children}
</code>
);
},
);

StudioCodeFragment.displayName = 'StudioCodeFragment';

export { StudioCodeFragment };
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { StudioCodeFragment } from './StudioCodeFragment';
export type { StudioCodeFragmentProps } from './StudioCodeFragment';
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { convertNumberToString, convertStringToNumber, isStringValidDecimalNumbe
import { type StudioTextfieldProps, StudioTextfield } from '../StudioTextfield';

export interface StudioDecimalInputProps extends Omit<StudioTextfieldProps, 'onChange'> {
description: string;
description?: string;
onChange: (value: number) => void;
value?: number;
validationErrorMessage: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.manualEditor textarea {
font-family: 'Courier New', monospace;
font-size: 0.8em;
}
Loading

0 comments on commit 57e33d0

Please sign in to comment.