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

Checkbox improvements #7775

Closed
wants to merge 42 commits into from
Closed

Conversation

ivarne
Copy link
Member

@ivarne ivarne commented Jan 7, 2022

When working on Altinn/app-frontend-react#789, I first needed to do some clanup. My first batch of changes was #7718 to remove the shortcut where formData is a string if the component is only configured with a single simpleBinding.

This is my next batch of changes where I try to clan up CheckboxContainerComponent, but unfortuenatly it had to build on the previous pull request that is not yet accepted. I'm not sure if you'd rather want a huge messy PR or multiple smaller, but now you have both.

Description

This PR is intended to fix the following changes in b575af0

  • useMemo instead of calculated state
  • Fix reference stability issues
  • Remove onBlur event (Does not exist on checkbox)
  • Remove custom focus functionality (I can't figure out what it does, and how that would be correct)

@jeevananthank I spent most of yesterday trying to figure out the issue in #3782 that the special focus handeling was introduced to try to fix. In this PR I removed the functionality, and I could not reproduce the issue afterwards. Would you be able to have a look?

Fixes

Verification

  • Your code builds clean without any errors or warnings
  • Manual testing done (required)
  • Relevant automated test added (if you find this hard, leave it and we'll help out)
  • All tests run green

Documentation

@ivarne ivarne force-pushed the checkbox-improvements branch from 9ab64e5 to d9909fc Compare January 7, 2022 07:42
@jeevananthank jeevananthank added the community-contribution-❤️ Contributions from developers outside the Altinn teams. label Jan 7, 2022
@jeevananthank
Copy link
Contributor

@ivarne #3782 is not reproducible in the updated code. but our automated tests are failing. Required fields are not validated on navigating between the form components and the error message: 'Feltet er påkrevd' is not presented to the user on navigation.

@ivarne
Copy link
Member Author

ivarne commented Jan 11, 2022

@jeevananthank Can you share more details? Its really hard to diagnose a failing test that I don't have access to (as far as I can see), and in code I wouldn't think my changes would affect.

@jeevananthank
Copy link
Contributor

@jeevananthank Can you share more details? Its really hard to diagnose a failing test that I don't have access to (as far as I can see), and in code I wouldn't think my changes would affect.

have a input field with required: true in the app -> point app to localhost version of app frontend -> Start the app instance -> tab through the input field -> error message that says the field is required is not presented.

* useMemo instead of calculated state
* Fix reference stability issues
* Remove onBlur event (Does not exist on checbox)
* Remove custom focus functionality (I can't figure out what it does, and how that would be correct)
@ivarne ivarne force-pushed the checkbox-improvements branch from 97acde6 to b148d93 Compare January 20, 2022 08:29
@ivarne
Copy link
Member Author

ivarne commented Jan 25, 2022

Any chance you could have another look at this PR now that #7718 is merged.

Comment on lines 93 to 94
// This effect should only run when new options are loaded
// eslint-disable-next-line react-hooks/exhaustive-deps
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this should be removed. If we later are introducing other dependencies that are needed, this eslint-disable might cause a bug since we might forget to add it to the list.

It looks to me it doesnt matter if this is run several times either, because you will only call the handleDataChange if you have not selected anything, so removing it should be fine I think.

Copy link
Member Author

Choose a reason for hiding this comment

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

So you think I should put handleDataChange in the dependency list, and just let the effect run on every render (it is not referentially stable)?

It might not be a catastrophe, but definetly seems like the wrong code to me. I could use a ref to make it still be correct

//Library Function
const usePrevious = (value:T)=>{
    const ref = React.useRef<T>(null);
    const previous = ref.current;
    ref.current = value;
    return previous;
};

---
// In render
const previousOptions = usePrevious(options);
React.useEffect(() => {
    const preselectedOption = options?.[props.preselectedOptionIndex]?.value;
    if (
      !selected &&
      options === previousOptions &&
      preselectedOption
    ) {
      props.handleDataChange(preselectedOption);
    }
}, [options, selected, props.handleDataChange]

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, I think handleDataChange should be set as a dependency. Ignoring it may lead to stale closures, which is very bad. Listing it may cause "unnescessary renders" because of the issue that its not referentially stable. If the unnescessary rerenders are causing performance issues, then that should be looked into, and the fix would probably be to wrap the handleDataChange in a useCallback. But if its not causing performance issues, I wouldnt worry about it.

Copy link
Member Author

Choose a reason for hiding this comment

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

Stale closures is a non-issue with useEffect, because we create a fresh closure on every render, regardless of the deps array. The question is how we decide when the effect should be executed. This effect needs to run only when the list of options changes, so that we can select the default option if nothing was previously selected. If options did not change, there is no need to update the state. The easiest way to ensure that requirement is to ensure that options is referentially stable and put only options in the deps array.

Copy link
Contributor

Choose a reason for hiding this comment

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

Stale closures is a non-issue with useEffect, because we create a fresh closure on every render, regardless of the deps array

The execution of the hook will be skipped, unless any items in the dependency array changed (or if you have an empty array, it will only be run once. If you dont provide an array, it will be run on every render). Stating that stale closures is a non-issue for useEffect is not correct.

This is why the exhaustive-deps rule exists -it is important to list all the dependencies, even functions, so the useEffect runs when something changes, to avoid stale closures.

This effect should run also when props.handleDataChange is changed as well. Some other code might run and change that function to do something else, f.ex call another API. If this useEffect does not react to that change, it will still call the old props.handleDataChange function. The example is a bit contrived, and its highly unlikely that the handleDataChange will do something else at some point, but it illustrates the point.

Adding the comment to disable the exhaustive deps might be fine right now, because we know that props.handleDataChange will in fact not change. But it creates problems later, when some new code is introduced here, that uses other props that may change between renders. You might not notice the comment is there and the check is disabled so you forget to list the new dependency, and you introduce a stale closure and potentially a bug.

You already have a check to see if an option is selected in the useEffect, so it should not matter if the effect is executed on every render.

Copy link
Member Author

@ivarne ivarne Jan 28, 2022

Choose a reason for hiding this comment

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

No, the examples you link to sets callbacks in useEffect and obviously those callbacks needs to be updated in case they change, because they will be called at a much later time. The closure passed to useEffect here calls handleDataChange immediatly after render and there is no chance that reference to be stale.

The comment to disable the check now is fine because we know handleDataChange will change.

I'm tired of this discussion. Can you just add a suggestion to how you want the code to be written, so I can report the behavioural errors as bugs instead?

Copy link
Contributor

@haakemon haakemon Jan 28, 2022

Choose a reason for hiding this comment

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

I'm tired of this discussion. Can you just add a suggestion to how you want the code to be written, so I can report the behavioural errors as bugs instead?

Whats with the attitude? I spend time on reviewing the code you wrote, and called out a problem - I think these types of replies are uncalled for. I just want to make sure we dont introduce new potential issues in the code, and not specifying the correct dependencies to the hooks array is a problem.

Either I am not understanding you correctly, or there is a misconception about how useEffect works. But I think the docs are pretty clear on this; if you do not specify every external dependency in the dependency array, the outcome is unpredictable. It does not matter that it "calls handleDataChange immediately after a render", because the entire useEffect hook might be skipped in the next render, because it only cares if options changes. If handleDataChange is changed and options stay the same, the useEffect will not be run and the current reference to handleDataChange will be stale.

However, as I said earlier, this is not the main problem with the code, since handleDataChange is higly unliketly to be changed. The main problem is future maintainability. New dependencies can be introduced which requires the useEffect to be called again. Without having the safetynet of exhaustive-deps, it is likely that this new dependency will be forgotten to be added. This is not a made up issue, this is something I've seen happen several times. This is also why I'm working on removing all the ignore statements for exhaustive-deps.

Copy link
Member Author

Choose a reason for hiding this comment

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

Can you please show me the code you believe to be correct? I have shown my attempt previously in this thread, but it requires an extra useRef (hidden in usePrevious), which I don't think adds any clarity or maintainability. Do you think my attempt is wrong in any way? As you see it includes props.handleDataChange, so that is not the big issue.

I'm tired of you not understanding that this effect is only intended to run once after the options have been loaded, in order to set the default choice (preselectedOptionIndex). When that option is set, the user should be allowed to deselect the option and pick another option (or leave it empty) if they want to.

PS: now that I think about it, the preselectedOptionIndex does not make any sense to use in checkbox anyway. For radio buttons it makes total sense, but with checkboxes it should be allowed to leave every box empty, and with the current implementation the preselected index will be checked again if you load the page with an empty selection.

const selectedHasValues = (select: string[]): boolean => {
return select.some((element) => element !== '');
};
const onDataChanged = (event: React.ChangeEvent<HTMLInputElement>) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

It would be good if this handler was renamed to handleDataChanged, as it is reacting to an event, not triggering it. I know we are not entirely consistent on these naming conventions in the codebase, but I am trying to improve that :)

Suggested change
const onDataChanged = (event: React.ChangeEvent<HTMLInputElement>) => {
const handleDataChanged = (event: React.ChangeEvent<HTMLInputElement>) => {

Copy link
Member Author

Choose a reason for hiding this comment

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

I just used the previous naming. I'll change this when the other issues are resolved.

Copy link
Member Author

Choose a reason for hiding this comment

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

Another issue is that we already have a function props.handleDataChange in this component. It would be somewhat confusing to have two functions with only one d in difference.

};

const inFocus = (index: number) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

I find it a bit difficult to follow all the logic here, and I'm not entirely sure what the details are around this, but from the looks of it, it seems like we are now missing some funcitonality for autofocus? The prop shouldFocus f.ex is not used anywhere.

Perhaps @lorang92 knows this area better and what is needed here?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yea, I didn't understand what this was doing either. The previous code mixes indexes in the selected array and the options array, so I'm pretty sure it is only correct by chance in some small special cases.
image

Copy link
Contributor

Choose a reason for hiding this comment

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

@ivarne / @haakemon i'm not familiar with this part of the code either. I can't really make sense of it. If regression tests run green my hunch here is that this could be removed.

Copy link
Contributor

Choose a reason for hiding this comment

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

@jeevananthank just verify that this issue has not resurfaced when testing: #3782

Copy link
Contributor

Choose a reason for hiding this comment

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

@ivarne ah, I see that you already mentioned that ❤️

Copy link
Contributor

Choose a reason for hiding this comment

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

@lorang92 #3782 is not reproducible in the updated code

RonnyB71 and others added 16 commits January 31, 2022 21:34
* fix: change so attributes har handled first when converting from XSD to Json Schema

* test: updated tests according to puttiing attributes first in Json Schema

* test: add test for verifying that c-sharp classes are generated the same way as before

* test: cleanup

* test: changed assert to use deserialization instead of comparing objects without data

* test: changed reference to problematic line when building in Azure

* chore: Updated Nuget package for code analysis to get rid of Warning NU1608: Detected package version outside of dependency constraint: Microsoft.CodeAnalysis.CSharp.Workspaces

* refactor: primarily renaming of tuple parameters to get rid of compiler warnings

Co-authored-by: Ronny Birkeli <ronny.birkeli@digdir.no>
* -Previously, GetResourcePolicies would always return the ResourcePolicy as long as the blobstorage path was valid. Now it also checks if the rules' appIds is in the request's list of appIds.
-Added a unit test

* fixed unit test

* Fixed indentation

* wrote better summary for a unit test

Co-authored-by: Ivar Tryti <oit@brreg.no>
Co-authored-by: Ivar Olav Tryti <aas-iotryti@ai-dev.no>
* added automated tests for integration point

* added integration point test to use cases

* Update eFormidling.js

* prettier

* Update eFormidling.js
Co-authored-by: tjololo <tjololo@users.noreply.github.com>
Bumps [Microsoft.IdentityModel.Protocols.OpenIdConnect](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet) from 6.15.0 to 6.15.1.
- [Release notes](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/releases)
- [Changelog](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/CHANGELOG.md)
- [Commits](AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet@v6.15.0...6.15.1)

---
updated-dependencies:
- dependency-name: Microsoft.IdentityModel.Protocols.OpenIdConnect
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [Microsoft.IdentityModel.Tokens](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet) from 6.15.0 to 6.15.1.
- [Release notes](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/releases)
- [Changelog](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/CHANGELOG.md)
- [Commits](AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet@v6.15.0...6.15.1)

---
updated-dependencies:
- dependency-name: Microsoft.IdentityModel.Tokens
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [System.IdentityModel.Tokens.Jwt](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet) from 6.15.0 to 6.15.1.
- [Release notes](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/releases)
- [Changelog](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/CHANGELOG.md)
- [Commits](AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet@v6.15.0...6.15.1)

---
updated-dependencies:
- dependency-name: System.IdentityModel.Tokens.Jwt
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [Microsoft.IdentityModel.Protocols.OpenIdConnect](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet) from 6.15.0 to 6.15.1.
- [Release notes](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/releases)
- [Changelog](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/CHANGELOG.md)
- [Commits](AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet@v6.15.0...6.15.1)

---
updated-dependencies:
- dependency-name: Microsoft.IdentityModel.Protocols.OpenIdConnect
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…7859)

Bumps [HtmlAgilityPack](https://github.com/zzzprojects/html-agility-pack) from 1.11.39 to 1.11.40.
- [Release notes](https://github.com/zzzprojects/html-agility-pack/releases)
- [Commits](zzzprojects/html-agility-pack@v1.11.39...v1.11.40)

---
updated-dependencies:
- dependency-name: HtmlAgilityPack
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* change namespace of apiexception

* read response as string, but not declared by type

* reinstated misdeleted char

* current progress

* added tests

* added more tests
Bumps [Microsoft.IdentityModel.Protocols.OpenIdConnect](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet) from 6.15.0 to 6.15.1.
- [Release notes](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/releases)
- [Changelog](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/CHANGELOG.md)
- [Commits](AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet@v6.15.0...6.15.1)

---
updated-dependencies:
- dependency-name: Microsoft.IdentityModel.Protocols.OpenIdConnect
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* updated deps

* first changes for delegation api

* test for get policies

* new tests for add, get, delete rules

* new test for get decision on delegated rule

delete policy

* updated deps

* rollback updated deps

will be handled as part of patching

* updated tests for get delegated rules

* updated authz requests

* updated get policies api
acn-sbuad and others added 5 commits January 31, 2022 21:34
* current progres

* current progress

* unit tests running green

* removed logging from test

* removed log  configuring in tests

* fixed pr comments & messages

* use class being tested in WebApplicationFactory
Co-authored-by: Ronny Birkeli <ronny.birkeli@digdir.no>
* chore: update npm packages in all solutions

* chore: revert wrong updates

* chore: add missing yarn lock files

* bump package.json version

Co-authored-by: Ronny Birkeli <ronny.birkeli@digdir.no>
Co-authored-by: Steffen Lorang Ekeberg <steffen.ekeberg@gmail.com>
@github-actions github-actions bot added area/api-expose area/authentication Area: Issues related to authentication in Altinn Studio area/authorization Area: Issues related to roles and rights on apps, such as who can instantiate, sign etc. area/dashboard Area: Related to the dashboard application area/text-editor Area: Related to creating, translating and editing texts. area/ui-editor Area: Related to the designer tool for assembling app UI in Altinn Studio. solution/apps Issues related to apps or the Altinn Apps infrastructures. solution/platform Issues related to Altinn Platform solutions. solution/studio/designer Issues related to the Altinn Studio Designer solution. solution/studio/repos Issues related to the Altinn Studio Repos solution (Gitea). labels Feb 1, 2022
@ivarne ivarne marked this pull request as draft February 3, 2022 14:04
@ivarne ivarne closed this Feb 9, 2022
@ivarne ivarne deleted the checkbox-improvements branch February 9, 2022 08:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/authentication Area: Issues related to authentication in Altinn Studio area/authorization Area: Issues related to roles and rights on apps, such as who can instantiate, sign etc. area/dashboard Area: Related to the dashboard application area/text-editor Area: Related to creating, translating and editing texts. area/ui-editor Area: Related to the designer tool for assembling app UI in Altinn Studio. community-contribution-❤️ Contributions from developers outside the Altinn teams. solution/apps Issues related to apps or the Altinn Apps infrastructures. solution/platform Issues related to Altinn Platform solutions. solution/studio/designer Issues related to the Altinn Studio Designer solution. solution/studio/repos Issues related to the Altinn Studio Repos solution (Gitea).
Projects
None yet
Development

Successfully merging this pull request may close these issues.