Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shifting complexity in expressions/hierarchy #943

Merged
merged 9 commits into from
Feb 24, 2023

Conversation

olemartinorg
Copy link
Contributor

@olemartinorg olemartinorg commented Feb 21, 2023

Description

This PR is rather large, and contains somewhat complex type-changes. I apologize for the lack of readability! All in all, the main points of interest in this PR:

  • I'm removing the NodeType type that was used everywhere in hierarchy.types.d.ts. The point of this type was to make it possible to switch between resolved/unresolved properties (basically supporting having the full hierarchy tree represented with dynamic expressions intact, or with them evaluated). After Expressions optimization and streamlining #861, there's almost no usage of unresolved layout hierarchies, as accessing the full resolved tree got to be cheap and easy.
  • With no real need for all the extra functions in hierarchy.ts, I'm moving some of them to a _private export, meant to symbolize that they're not really meant to be used outside of where they're in use now (unit tests). Please use the end result (the fully resolved, deep tree/hierarchy of components), not any of the intermediate steps.
  • The ExprResolved type we used to have was cool and all, but it performed somewhat poorly. When combining it with other types, especially MaybeSpecificItem in ExprContext (again, coming from Expressions optimization and streamlining #861), you quickly reached the limits of TypeScript (or some other limit, who knows), and adding another layer depth of complexity to these types would break them. I don't like that the new, shining, and more future-proof framework we're building on is hard to use and extend, and after bumping my head against this thing, I decided to simplify the types for the layout hierarchy as much as I could, to hopefully invite further developments in that direction. To do that, I had to move/shift the complexity to the old-school methods of manipulating simple components/layouts directly. This is most clearly visible in this PR by the next bullet point:
  • The types used for layout components (ILayoutComp*, ILayoutComponent) used to be defined such that they could contain either a concrete value (i.e. required: true) or an expression (i.e. required: ["equals", true, false]). This has now changed, and layout components are defined to have places that can contain expressions, but in order to actually use such an object, you at some point have to indicate to the type-system weather or not you expect these expressions to be resolved or not. Before, resolved expressions could be assignable directly to an ILayoutComp* type, and you could sometimes get away with just using the layout redux state directly (in case you didn't have any expressions in the layout). This could cause bugs and requires code that is expression-aware to be the most complex. I've moved this complexity so that almost every code using the layout types need to wrap them in either ExprResolved<...> (usually via HComponent or AnyItem/LayoutNode) or ExprUnresolved<...>. for resolved and unresolved (as-from-the-api) expressions, respectively.
  • With NodeType gone, and with the complexity moved to legacy-style code via a forced ExprUnresolved<...> type, I found and deprecated a whole lot of code that I think should go away when we slowly clean up usages. It should also discourage further use of the older functionality, and encourage usage of the layout hierarchy.

Related Issue(s)

Verification/QA

  • Manual functionality testing
    • I have tested these changes manually
      • (That means, I tested evalExpression() in the console, which had minor changes, and I tested and fixed the failing cypress tests)
    • Creator of the original issue (or service owner) has been contacted for manual testing (or will be contacted when released in alpha)
    • No testing done/necessary
  • Automated tests
    • Unit test(s) have been added/updated
    • Cypress E2E test(s) have been added/updated
    • No automatic tests are needed here (no functional changes/additions)
    • I want someone to help me make some tests
  • UU/WCAG (follow these guidelines until we have our own)
    • I have tested with a screen reader/keyboard navigation/automated wcag validator
    • No testing done/necessary (no DOM/visual changes)
    • I want someone to help me perform accessibility testing
  • User documentation @ altinn-studio-docs
    • Has been added/updated
    • No functionality has been changed/added, so no documentation is needed
    • I will do that later/have created an issue
  • Changes/additions to component properties
    • Changes are reflected in both src/layout/layout.d.ts and layout.schema.v1.json, and these are all backwards-compatible
    • No changes made
  • Support in Altinn Studio
    • Issue(s) created for support in Studio
    • This change/feature does not require any changes to Altinn Studio
  • Sprint board
    • The original issue (or this PR itself) has been added to the Team Apps project and to the current sprint board
    • I don't have permissions to do that, please help me out
  • Labels
    • I have added a kind/* label to this PR for proper release notes grouping
    • I don't have permissions to add labels, please help me out

Ole Martin Handeland added 5 commits February 21, 2023 00:13
…ly resolved layout sets seems to be the ones in use (and using multiple of these functions will just serve to make refactoring harder)
… node/component/hierarchy is resolved or not, when the new default is that everything is resolved all the time. It's just at some points in the hierarchy.ts code that expressions are not yet resolved.
Using an enum (currently BaseValue) to indicate places in the layout that are supposed to have expressions instead of the fancy ExpressionOr<...> type that allowed for concrete values AND expressions. Now, the ILayoutComponent type itself does not support concrete values, expressions, or indicates that expressions are resolved. To do that, you have to wrap the type in ExprResolved<...> or ExprUnresolved<...> (the latter is deprecated, and we're hereby shifting the complexity of using non-resolved component definitions over to the older deprecated code). This makes working with the newer hierarchy system cleaner and easier than working with legacy code, hopefully increasing adoption and making for better developer experience.
Turns out the type I declared does devolves the enum to just a string, losing type-safety for the returnType assignment. I found and fixed the cases manually here. I tried, but failed, to enforce this using TypeScript, sadly.
This will better indicate the intended use for this enum, especially in component definitions.
@olemartinorg olemartinorg added area/logic related to logic/dynamics/expressions kind/other Pull requests containing chores/repo structure/other changes ignore-for-release Pull requests to be ignored in release notes area/layout related to layouts/components labels Feb 21, 2023
Ole Martin Handeland added 2 commits February 21, 2023 21:46
…t resource bindings are automatically resolved according to row/index, so no need to run this again.
… and likert.ts cypress tests, so I'm not bothering fixing the mocking/test bootstrapping in order to give this real-world input so that it works again (the text variables are replaced by the node hierarchy now).
@olemartinorg olemartinorg marked this pull request as ready for review February 22, 2023 09:34
Comment on lines +196 to +199
layoutComponent?.getComponentType() === ComponentType.Presentation &&
formComponentLegacy.type !== 'Summary' &&
formComponentLegacy.type !== 'Group'
) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think checking that the type is not Summary or Group is necessary here as they do not have a ComponentType, so they would fail because null !== ComponentType.Presentation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're probably right! I did that to appease TypeScript, although it's obvious that the extra checks would always pass. I'll look over it once more, hopefully after you merge #940 and I do one more pass over this thing. Thanks for letting me know!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked now, and it's still needed. TypeScript doesn't know that layoutComponent and formComponentLegacy is supposed to be the same thing (in many ways), so checking the type of layoutComponent does not narrow the type of formComponentLegacy (which is used in the implementation below). So these checks will make sure to narrow the type of formComponentLegacy so that GenericComponent can render it.

Copy link
Member

@bjosttveit bjosttveit left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me as long as the tests are passing, can't really tell just by looking 😅

Ole Martin Handeland added 2 commits February 24, 2023 10:42
# Conflicts:
#	src/components/message/ErrorReport.tsx
#	src/features/pdf/AutomaticPDFLayout.tsx
#	src/features/pdf/CustomPDFLayout.tsx
#	src/utils/formComponentUtils.ts
@sonarqubecloud
Copy link

SonarCloud Quality Gate failed.    Quality Gate failed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell C 117 Code Smells

68.3% 68.3% Coverage
0.0% 0.0% Duplication

@olemartinorg olemartinorg merged commit 06e721d into main Feb 24, 2023
@olemartinorg olemartinorg deleted the chore/expr-type-cleanup branch February 24, 2023 11:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/layout related to layouts/components area/logic related to logic/dynamics/expressions ignore-for-release Pull requests to be ignored in release notes kind/other Pull requests containing chores/repo structure/other changes
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

3 participants