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

Re-implement Modal component using HTMLDialogElement (#461) #544

Open
wants to merge 8 commits into
base: master
Choose a base branch
from

Conversation

bedrich-schindler
Copy link
Contributor

@bedrich-schindler bedrich-schindler commented May 30, 2024

Closes #461, closes #537.

@mbohal mbohal self-requested a review June 3, 2024 08:43
src/theme.scss Outdated Show resolved Hide resolved
src/components/Modal/Modal.module.scss Outdated Show resolved Hide resolved
src/components/Modal/__tests__/Modal.test.jsx Outdated Show resolved Hide resolved
@bedrich-schindler
Copy link
Contributor Author

bedrich-schindler commented Jun 3, 2024

Update CSS properties in README.md.

Done ✅

Copy link
Member

@adamkudrna adamkudrna left a comment

Choose a reason for hiding this comment

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

Nice job! 👏🏻 Even with the decision of not having the closed Modal in the DOM. 👍

I just think we cannot remove the click-on-backdrop-to-close functionality.

src/components/Modal/Modal.jsx Outdated Show resolved Hide resolved
src/components/Modal/Modal.jsx Outdated Show resolved Hide resolved
src/components/Modal/Modal.jsx Outdated Show resolved Hide resolved
src/components/Modal/Modal.jsx Outdated Show resolved Hide resolved
src/components/Modal/Modal.jsx Outdated Show resolved Hide resolved
src/components/Modal/Modal.module.scss Outdated Show resolved Hide resolved
src/components/Modal/_settings.scss Outdated Show resolved Hide resolved
src/components/Modal/__tests__/Modal.test.jsx Outdated Show resolved Hide resolved
src/components/Modal/README.md Show resolved Hide resolved
src/components/Modal/Modal.jsx Show resolved Hide resolved
@bedrich-schindler
Copy link
Contributor Author

PR updated:

* Introduced allowCloseOnBackdropClick and allowCloseOnEscapeKey prop, both set to true by default. It allows end-user to configure modal in such way that he/she can prevent closing by clicking on backdrop or pressing Escape key.
* Improved blocking of firing click events on primary and close button as there were missing conditions checking whether the button is disabled (it was there for a long time)

  • The biggest problem was with propagation of events, Enter was mistakenly closing dialog in unwanted cases. I can talk about it on Monday if you wish.

While I introduces allowCloseOnBackdropClick and allowCloseOnEscapeKey, I am thinking about allowProceedOnEnterKey (or something like that) to disable Enter functionality which can be unwanted in some scenarios.

@bedrich-schindler
Copy link
Contributor Author

Just for info, tests need to be updated in #545 as new test environment is necessary.

Copy link
Member

@adamkudrna adamkudrna left a comment

Choose a reason for hiding this comment

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

I have a note regarding forms inside dialog, otherwise OK for me.

src/components/Modal/README.md Show resolved Hide resolved
@bedrich-schindler
Copy link
Contributor Author

Can @adamkudrna or anybody else help me with this?

image

I returned to this to complete the pull request but suddenly, I have all the texts inside of Modal in white even though there is no change in the code. master is OK. Does anybody know why? I do not want to rebased it until this is resolved.

@adamkudrna
Copy link
Member

Does anybody know why? I do not want to rebased it until this is resolved.

I added a fix. It seems to happen only when the docs is switched to dark mode.

@bedrich-schindler
Copy link
Contributor Author

I have rebased and squashed it. I'm gonna perform self-review first as I have seen that documentation is not up-to-date with latest changes and then I will request review from you-

@bedrich-schindler
Copy link
Contributor Author

Since rebase:

I fixed submitting dialog when form fields are present. It is not working either on master. Formerly, it was part of auto focus hook which I have modified. It was also containing focus trap that it can be removed now. Now, this functionality is part of dialogOnKeyDownHandler.js.

Then I have improved documentation and did minor fixes.

@bedrich-schindler
Copy link
Contributor Author

I made separate commit (2514fd7) to demonstrate example with native form. Personally, I would not encourage this approach as every application differs in the way how it handles forms, state, validations and so. But it is true that it can be useful for example in that way, that native form will not be submitted until HTML validations are passing.

We had 4 examples of different kind, so I added native form as fifth and atypically explained it in the example. Even though it is atypical, I would leave it there as it is only place how we can describe example with modal with creating explicit section. And we do not want to handle all possibilities in the documentation, so it compromise I think.

@adamkudrna @mbohal, you can review the pull request and tell whether to include this native form example or drop it.

@bedrich-schindler
Copy link
Contributor Author

I fixed submitting dialog when form fields are present. It is not working either on master. Formerly, it was part of auto focus hook which I have modified. It was also containing focus trap that it can be removed now. Now, this functionality is part of dialogOnKeyDownHandler.js.

I just want to mention (it will be part of commit message) that even though HTML Dialog states that it automatically focuses first focusable element in dialog, it does not work as our solution. The problem is that it autofocuses only some elements and for example states when there are only buttons and no input fields do not work. So we have to keep this functionality and we are not able to remove it!

The only question is: Do we want to have autoFocus always enabled? Personally, I would suggest removing autoFocus prop and make it mandatory as native solution would automatically focuses some elements even when our autoFocus is off.

@mbohal
Copy link
Contributor

mbohal commented Jan 20, 2025

The only question is: Do we want to have autoFocus always enabled? Personally, I would suggest removing autoFocus prop and make it mandatory as native solution would automatically focuses some elements even when our autoFocus is off.

Agreed we want to make autoFocus always on.

src/components/Modal/Modal.jsx Show resolved Hide resolved
src/components/Modal/_hooks/useModalFocus.js Outdated Show resolved Hide resolved
…` tests (#461)

`happy-dom` is used due to missing HtmlDialogElement support in `jest`.
Just to mention, `happy-dom` provides partial support for dialog element,
so not all test can be implemented.
@bedrich-schindler
Copy link
Contributor Author

I have merged PR with happy-dom into this PR.

@bedrich-schindler
Copy link
Contributor Author

After discussion with @mbohal, we decided not to change our autoFocus functionality. Even though native autofocus functionality was introduced into , it does not work as good as out autoFocus functionality because it selects first item which is typically close button in our case, so that's the reason we want to leave it as it is.

I dug deep into in again @mbohal and I found out, that with autoFocus disabled (through props, not through deleting whole functionality), our autoFocus internally forces focus on element, so it still can prevent native behavior. I had been testing it with completely removed functionality and probably cached page, so I had been obtaining wrong results before.

Copy link
Member

@adamkudrna adamkudrna left a comment

Choose a reason for hiding this comment

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

Well done with beautiful demos! I only have two major requests/comments:

  • Make a new "Forms" section. Forms in dialogs are a common use case. This is why I click "Request changes" here.
  • Consider if we can get more out of the native functionality of the <dialog>. But maybe you have done an in-depth reserach and I see it wrong.

Thank you!

height: 100vh;
.root[open] {
display: flex;
animation: fade-in theme.$animation-duration ease-out;
Copy link
Member

Choose a reason for hiding this comment

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

Please:

  • add a note the animation is currently only supported in Chrome, Edge and Opera (Blink rendering engine),
  • wrap the animation into @media (prefers-reduced-motion: no-preference).

background: theme.$backdrop-background;
animation: inherit;
Copy link
Member

Choose a reason for hiding this comment

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

+1 for inheritance. Just wrap the line into @media (prefers-reduced-motion: no-preference) please.

this feature off, set the `autofocus` prop to `false`.

- Modal **submits the form when the user presses the `Enter` key** . The primary
button is clicked in this case. To turn this feature off, set the
Copy link
Member

Choose a reason for hiding this comment

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

I think the click is just emulated, isn't it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is not. click() function is called directly on HTML button, it is not emulated, it's real click that also triggers its own click event. So I would not call it emulated.

@mbohal can correct me, but emulated event would be dispatchEvent(event), but not click().

`allowPrimaryActionOnEnterKey` prop to `false`.

- Modal **closes when the user presses the `Escape` key**. The close button is
clicked in this case. To turn this feature off, set the `allowCloseOnEscapeKey`
Copy link
Member

Choose a reason for hiding this comment

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

The same about "clicking" here.

@@ -225,9 +237,33 @@ React.createElement(() => {
</ModalHeader>
<ModalBody>
<ModalContent>
<FormLayout fieldLayout="horizontal">
<TextField label="Username" />
<FormLayout fieldLayout="horizontal" labelWidth="limited">
Copy link
Member

Choose a reason for hiding this comment

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

Thanks for extending the form with new fields!

connect it with the form.
</p>
<p>
Although we do not encourage using this approach, it is still
Copy link
Member

Choose a reason for hiding this comment

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

Why don't we encourage the native solution? Is there something wrong when combining it with React? I'd like to see the reason mentioned here.

Comment on lines +162 to +165
<Button
label="Launch modal as native form"
onClick={() => setModalOpen(5)}
/>
Copy link
Member

Choose a reason for hiding this comment

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

I think this section has become too long and deserves splitting into more blocks. From my experience, using the modal dialogs with forms is a frequent use case and it deserves a prominent place in the design system docs. This is how I see it:

  1. Create a new section called "Forms" right before the "Mouse and Keyboard Control" section.
  2. Explain shortly the Modal can be used with or without the native <form> element. If you can also tell what are the use cases, even better.
  3. Extract the text from the last example (native form) so it's right in the document and not hidden in the demo.
  4. In the original section, please move the "Launch modal as dialog" example to the first place. Ever since we have the Modal component, I always click on the first button and immendiately hate myself for having to wait for the blocking modal to close 🙈.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In this case, let's discuss this on Monday. We explicitly approved this on last meeting where you were supposed to be ...

TLDR; I do not encourage users to use native form behavior as it is good for plain HTML apps without JS (or with minimal JS), but not for React apps where the principles are different.

If we create Forms section, what will we do with this section? There are supported to be typical examples. If we create separate section, I would be duplicated ...

Comment on lines +17 to +23
const dialogRect = dialogRef.current.getBoundingClientRect();
const isClickedOnBackdrop = dialogRef.current === e.target && (
e.clientX < dialogRect.left
|| e.clientX > dialogRect.right
|| e.clientY < dialogRect.top
|| e.clientY > dialogRect.bottom
);
Copy link
Member

@adamkudrna adamkudrna Jan 22, 2025

Choose a reason for hiding this comment

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

Do we still need this with native dialog? The <dialog> triggers the close event so I was wondering if we could listen on it/use it?

https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog#handling_the_return_value_from_the_dialog

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If it wasn't needed, it wouldn't be here. Neither close nor cancel event is called on backdrop click. If you want to close dialog by clicking on backdrop, you have to listen to click event and detect user clicked outside of dialog element or use different method of detection. However, this is most common approach.

It is quite hard to decide what to comment. This is default behavior/knowledge and developer is supposed to know this if he/she want to dig into it. Now, each line of each event handler is commented. But I would add docblock for each function to make it crystal clear.

@@ -4,7 +4,7 @@ const StyleLintPlugin = require('stylelint-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const VisualizerPlugin = require('webpack-visualizer-plugin2');

const MAX_DEVELOPMENT_OUTPUT_SIZE = 3300000;
const MAX_DEVELOPMENT_OUTPUT_SIZE = 3400000;
Copy link
Member

Choose a reason for hiding this comment

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

How come? 🤔

obrazek

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
BC Breaking change feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

In docs Modal is not dismissed by pressing Esc Use native <dialog> element for Modal
4 participants