diff --git a/docs/src/actionability.md b/docs/src/actionability.md
index 79fba5e80d..3acbcf644f 100644
--- a/docs/src/actionability.md
+++ b/docs/src/actionability.md
@@ -32,7 +32,20 @@ Some actions like [`method: Page.click`] support `force` option that disables no
| textContent | Yes | - | - | - | - | - |
| type | Yes | - | - | - | - | - |
-
+You can check the actionability state of the element using one of the following methods:
+
+- [`method: ElementHandle.isChecked`]
+- [`method: ElementHandle.isDisabled`]
+- [`method: ElementHandle.isEditable`]
+- [`method: ElementHandle.isEnabled`]
+- [`method: ElementHandle.isHidden`]
+- [`method: ElementHandle.isVisible`]
+- [`method: Page.isChecked`]
+- [`method: Page.isDisabled`]
+- [`method: Page.isEditable`]
+- [`method: Page.isEnabled`]
+- [`method: Page.isHidden`]
+- [`method: Page.isVisible`]
### Visible
diff --git a/docs/src/dialogs.md b/docs/src/dialogs.md
new file mode 100644
index 0000000000..6e69c141fa
--- /dev/null
+++ b/docs/src/dialogs.md
@@ -0,0 +1,94 @@
+---
+id: dialogs
+title: "Dialogs"
+---
+
+Playwright can interact with the web page dialogs such as [`alert`](https://developer.mozilla.org/en-US/docs/Web/API/Window/alert), [`confirm`](https://developer.mozilla.org/en-US/docs/Web/API/Window/confirm), [`prompt`](https://developer.mozilla.org/en-US/docs/Web/API/Window/prompt) as well as [`beforeunload`](https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event) confirmation.
+
+
+
+## alert(), confirm(), prompt() dialogs
+
+You can register a dialog handler before the action that triggers the dialog to accept or decline it.
+
+```js
+page.on('dialog', dialog => dialog.accept());
+await page.click('button');
+```
+
+```python async
+page.on("dialog", lambda dialog: dialog.accept())
+await page.click("button")
+```
+
+```python sync
+page.on("dialog", lambda dialog: dialog.accept())
+page.click("button")
+```
+
+:::note
+If your action, be it [`method: Page.click`], [`method: Page.evaluate`] or any other, results in a dialog, the action will stall until the dialog is handled. That's because dialogs in Web are modal and block further page execution until they are handled.
+:::
+
+As a result, following snippet will never resolve:
+
+:::warn
+WRONG!
+:::
+
+```js
+await page.click('button'); // Will hang here
+page.on('dialog', dialog => dialog.accept())
+```
+
+:::warn
+WRONG!
+:::
+
+```python async
+await page.click("button") # Will hang here
+page.on("dialog", lambda dialog: dialog.accept())
+```
+
+```python sync
+page.click("button") # Will hang here
+page.on("dialog", lambda dialog: dialog.accept())
+```
+
+#### API reference
+
+- [`Dialog`]
+- [`method: Dialog.accept`]
+- [`method: Dialog.dismiss`]
+
+## beforeunload dialog
+
+When [`method: Page.close`] is invoked with the truthy [`option: runBeforeUnload`] value, it page runs its unload handlers. This is the only case when [`method: Page.close`] does not wait for the page to actually close, because it might be that the page stays open in the end of the operation.
+
+You can register a dialog handler to handle the beforeunload dialog yourself:
+
+```js
+page.on('dialog', async dialog => {
+ assert(dialog.type() === 'beforeunload');
+ await dialog.dismiss();
+});
+await page.close({runBeforeUnload: true});
+```
+
+```python async
+async def handle_dialog(dialog):
+ assert dialog.type == 'beforeunload'
+ await dialog.dismiss()
+
+page.on('dialog', lambda: handle_dialog)
+await page.close(run_before_unload=True)
+```
+
+```python sync
+def handle_dialog(dialog):
+ assert dialog.type == 'beforeunload'
+ dialog.dismiss()
+
+page.on('dialog', lambda: handle_dialog)
+page.close(run_before_unload=True)
+```
diff --git a/docs/src/downloads.md b/docs/src/downloads.md
new file mode 100644
index 0000000000..22ea7bdfe1
--- /dev/null
+++ b/docs/src/downloads.md
@@ -0,0 +1,77 @@
+---
+id: downloads
+title: "Downloads"
+---
+
+:::note
+For uploading files, see the [uploading files](./input.md#upload-files) section.
+:::
+
+For every attachment downloaded by the page, [`event: Page.download`] event is emitted. If you create a browser context
+with the [`option: acceptDownloads`] set, all these attachments are going to be downloaded into a temporary folder. You
+can obtain the download url, file system path and payload stream using the [Download] object from the event.
+
+You can specify where to persist downloaded files using the [`option: downloadsPath`] option in [`method: BrowserType.launch`].
+
+:::note
+Unless [`option: downloadsPath`] is set, downloaded files are deleted when the browser context that produced them is closed.
+:::
+
+Here is the simplest way to handle the file download:
+
+```js
+const [ download ] = await Promise.all([
+ // Start waiting for the download
+ page.waitForEvent('download'),
+ // Perform the action that initiates download
+ page.click('button#delayed-download')
+]);
+// Wait for the download process to complete
+const path = await download.path();
+```
+
+```python async
+# Start waiting for the download
+async with page.expect_download() as download_info:
+ # Perform the action that initiates download
+ await page.click("button#delayed-download")
+download = await download_info.value
+# Wait for the download process to complete
+path = await download.path()
+```
+
+```python sync
+# Start waiting for the download
+with page.expect_download() as download_info:
+ # Perform the action that initiates download
+ page.click("button#delayed-download")
+download = download_info.value
+# Wait for the download process to complete
+path = download.path()
+```
+
+#### Variations
+
+If you have no idea what initiates the download, you can still handle the event:
+
+```js
+page.on('download', download => download.path().then(console.log));
+```
+
+```python async
+async def handle_download(download):
+ print(await download.path())
+page.on("download", handle_download)
+```
+
+```python sync
+page.on("download", lambda download: print(download.path()))
+```
+
+Note that handling the event forks the control flow and makes script harder to follow. Your scenario might end while you
+are downloading a file since your main control flow is not awaiting for this operation to resolve.
+
+#### API reference
+- [Download]
+- [`event: Page.download`]
+- [`method: Page.waitForEvent`]
diff --git a/docs/src/emulation.md b/docs/src/emulation.md
index 3e9ebf1dc9..9f86457717 100644
--- a/docs/src/emulation.md
+++ b/docs/src/emulation.md
@@ -1,6 +1,6 @@
---
id: emulation
-title: "Device and environment emulation"
+title: "Emulation"
---
Playwright allows overriding various parameters of the device where the browser is running:
diff --git a/docs/src/network.md b/docs/src/network.md
index 1559e65497..d6ea05cb5e 100644
--- a/docs/src/network.md
+++ b/docs/src/network.md
@@ -43,68 +43,6 @@ page.goto("https://example.com")
#### API reference
- [`method: Browser.newContext`]
-
-
-## Handle file downloads
-
-```js
-const [ download ] = await Promise.all([
- page.waitForEvent('download'), // <-- start waiting for the download
- page.click('button#delayed-download') // <-- perform the action that directly or indirectly initiates it
-]);
-const path = await download.path();
-```
-
-```python async
-# Start waiting for the download
-async with page.expect_download() as download_info:
- # Perform the action that directly or indirectly initiates it
- await page.click("button#delayed-download")
-download = await download_info.value
-path = await download.path()
-```
-
-```python sync
-# Start waiting for the download
-with page.expect_download() as download_info:
- # Perform the action that directly or indirectly initiates it
- page.click("button#delayed-download")
-download = download_info.value
-path = download.path()
-```
-
-For every attachment downloaded by the page, [`event: Page.download`] event is emitted. If you create a browser context
-with the [`option: acceptDownloads`] set, all these attachments are going to be downloaded into a temporary folder. You
-can obtain the download url, file system path and payload stream using the [Download] object from the event.
-
-#### Variations
-
-If you have no idea what initiates the download, you can still handle the event:
-
-```js
-page.on('download', download => download.path().then(console.log));
-```
-
-```python async
-async def handle_download(download):
- print(await download.path())
-page.on("download", handle_download)
-```
-
-```python sync
-page.on("download", lambda download: print(download.path()))
-```
-
-Note that handling the event forks the control flow and makes script harder to follow. Your scenario might end while you
-are downloading a file since your main control flow is not awaiting for this operation to resolve.
-
-#### API reference
-- [Download]
-- [`event: Page.download`]
-- [`method: Page.waitForEvent`]
-
-
-
## Network events
You can monitor all the requests and responses: