ReUI alignment: AlertDialog for destructive confirms, Input for date range, ghost Button for Alert close
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
This commit is contained in:
1 parent
7638939273
commit
59f882e58c
14 files changed
+71
-40
No files matched your search
@@ -217,7 +217,7 @@ describe('Apps: uninstalling', () => {
|
|||||||
const menu = await openMenu('What Is My Dns')
|
const menu = await openMenu('What Is My Dns')
|
||||||
await userEvent.click(menu.getByRole('menuitem', { name: 'Uninstall' }))
|
await userEvent.click(menu.getByRole('menuitem', { name: 'Uninstall' }))
|
||||||
await userEvent.click(
|
await userEvent.click(
|
||||||
within(screen.getByRole('dialog')).getByRole('button', { name: 'Uninstall' }),
|
within(screen.getByRole('alertdialog')).getByRole('button', { name: 'Uninstall' }),
|
||||||
)
|
)
|
||||||
expect(spy.mock.calls[0][1]).toBe('What Is My Dns')
|
expect(spy.mock.calls[0][1]).toBe('What Is My Dns')
|
||||||
expect(
|
expect(
|
||||||
@@ -484,7 +484,7 @@ describe('Apps: the store', () => {
|
|||||||
await userEvent.click(row.getByRole('button', { name: 'Uninstall' }))
|
await userEvent.click(row.getByRole('button', { name: 'Uninstall' }))
|
||||||
// The confirmation stacks over the store's dialog.
|
// The confirmation stacks over the store's dialog.
|
||||||
await userEvent.click(
|
await userEvent.click(
|
||||||
within(screen.getAllByRole('dialog').at(-1)!).getByRole('button', { name: 'Uninstall' }),
|
within(screen.getAllByRole('alertdialog').at(-1)!).getByRole('button', { name: 'Uninstall' }),
|
||||||
)
|
)
|
||||||
expect(
|
expect(
|
||||||
await screen.findByText("DNS application 'NO DATA' was uninstalled successfully."),
|
await screen.findByText("DNS application 'NO DATA' was uninstalled successfully."),
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ describe('Rules', () => {
|
|||||||
const remove = vi.spyOn(zonelists, 'deleteDomain').mockResolvedValue(OK)
|
const remove = vi.spyOn(zonelists, 'deleteDomain').mockResolvedValue(OK)
|
||||||
draw(NO_MODIFY)
|
draw(NO_MODIFY)
|
||||||
await userEvent.click(await screen.findByRole('button', { name: 'Delete ads.example.com' }))
|
await userEvent.click(await screen.findByRole('button', { name: 'Delete ads.example.com' }))
|
||||||
const dialog = await screen.findByRole('dialog')
|
const dialog = await screen.findByRole('alertdialog')
|
||||||
expect(dialog).toHaveTextContent("Are you sure you want to delete the blocked zone 'ads.example.com'?")
|
expect(dialog).toHaveTextContent("Are you sure you want to delete the blocked zone 'ads.example.com'?")
|
||||||
await userEvent.click(within(dialog).getByRole('button', { name: 'Delete' }))
|
await userEvent.click(within(dialog).getByRole('button', { name: 'Delete' }))
|
||||||
expect(remove).toHaveBeenCalledWith('blocked', 'T', 'ads.example.com')
|
expect(remove).toHaveBeenCalledWith('blocked', 'T', 'ads.example.com')
|
||||||
@@ -117,7 +117,7 @@ describe('Rules', () => {
|
|||||||
draw()
|
draw()
|
||||||
await userEvent.click(await screen.findByRole('button', { name: 'Flush' }))
|
await userEvent.click(await screen.findByRole('button', { name: 'Flush' }))
|
||||||
await userEvent.click(screen.getByRole('menuitem', { name: 'Allowed zones' }))
|
await userEvent.click(screen.getByRole('menuitem', { name: 'Allowed zones' }))
|
||||||
expect(await screen.findByRole('dialog')).toHaveTextContent('Are you sure you want to flush the entire Allowed zone?')
|
expect(await screen.findByRole('alertdialog')).toHaveTextContent('Are you sure you want to flush the entire Allowed zone?')
|
||||||
})
|
})
|
||||||
|
|
||||||
/* The sentences Allowed and Blocked said from their own headers, character for
|
/* The sentences Allowed and Blocked said from their own headers, character for
|
||||||
@@ -140,7 +140,7 @@ describe('Rules', () => {
|
|||||||
draw()
|
draw()
|
||||||
await userEvent.click(await screen.findByRole('button', { name: 'Flush' }))
|
await userEvent.click(await screen.findByRole('button', { name: 'Flush' }))
|
||||||
await userEvent.click(screen.getByRole('menuitem', { name: 'Blocked zones' }))
|
await userEvent.click(screen.getByRole('menuitem', { name: 'Blocked zones' }))
|
||||||
expect(await screen.findByRole('dialog')).toHaveTextContent('Are you sure you want to flush the entire Blocked zone?')
|
expect(await screen.findByRole('alertdialog')).toHaveTextContent('Are you sure you want to flush the entire Blocked zone?')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('flushing Allowed says so with upstream sentence', async () => {
|
it('flushing Allowed says so with upstream sentence', async () => {
|
||||||
@@ -149,7 +149,7 @@ describe('Rules', () => {
|
|||||||
draw()
|
draw()
|
||||||
await userEvent.click(await screen.findByRole('button', { name: 'Flush' }))
|
await userEvent.click(await screen.findByRole('button', { name: 'Flush' }))
|
||||||
await userEvent.click(screen.getByRole('menuitem', { name: 'Allowed zones' }))
|
await userEvent.click(screen.getByRole('menuitem', { name: 'Allowed zones' }))
|
||||||
await userEvent.click(within(await screen.findByRole('dialog')).getByRole('button', { name: 'Flush' }))
|
await userEvent.click(within(await screen.findByRole('alertdialog')).getByRole('button', { name: 'Flush' }))
|
||||||
expect(await screen.findByText('Allowed zone was flushed successfully.')).toBeInTheDocument()
|
expect(await screen.findByText('Allowed zone was flushed successfully.')).toBeInTheDocument()
|
||||||
expect(flush).toHaveBeenCalledWith('allowed', 'T')
|
expect(flush).toHaveBeenCalledWith('allowed', 'T')
|
||||||
})
|
})
|
||||||
@@ -194,7 +194,7 @@ describe('Rules', () => {
|
|||||||
await userEvent.click(screen.getByRole('button', { name: 'Tree' }))
|
await userEvent.click(screen.getByRole('button', { name: 'Tree' }))
|
||||||
const tree = await screen.findByRole('region', { name: 'Records' })
|
const tree = await screen.findByRole('region', { name: 'Records' })
|
||||||
await userEvent.click(within(tree).getByRole('button', { name: 'Delete' }))
|
await userEvent.click(within(tree).getByRole('button', { name: 'Delete' }))
|
||||||
await userEvent.click(within(await screen.findByRole('dialog')).getByRole('button', { name: 'Delete' }))
|
await userEvent.click(within(await screen.findByRole('alertdialog')).getByRole('button', { name: 'Delete' }))
|
||||||
await vi.waitFor(() => expect(read).toHaveBeenCalledTimes(4))
|
await vi.waitFor(() => expect(read).toHaveBeenCalledTimes(4))
|
||||||
expect(read).toHaveBeenLastCalledWith('allowed', 'T', 'dev.cluster.test')
|
expect(read).toHaveBeenLastCalledWith('allowed', 'T', 'dev.cluster.test')
|
||||||
await userEvent.click(screen.getByRole('button', { name: 'List' }))
|
await userEvent.click(screen.getByRole('button', { name: 'List' }))
|
||||||
@@ -215,7 +215,7 @@ describe('Rules', () => {
|
|||||||
await userEvent.click(screen.getByRole('button', { name: 'Tree' }))
|
await userEvent.click(screen.getByRole('button', { name: 'Tree' }))
|
||||||
const tree = await screen.findByRole('region', { name: 'Records' })
|
const tree = await screen.findByRole('region', { name: 'Records' })
|
||||||
await userEvent.click(within(tree).getByRole('button', { name: 'Delete' }))
|
await userEvent.click(within(tree).getByRole('button', { name: 'Delete' }))
|
||||||
await userEvent.click(within(await screen.findByRole('dialog')).getByRole('button', { name: 'Delete' }))
|
await userEvent.click(within(await screen.findByRole('alertdialog')).getByRole('button', { name: 'Delete' }))
|
||||||
const said = await screen.findAllByText("Blocked zone 'ads.test' was deleted successfully.")
|
const said = await screen.findAllByText("Blocked zone 'ads.test' was deleted successfully.")
|
||||||
expect(said).toHaveLength(1)
|
expect(said).toHaveLength(1)
|
||||||
const bar = screen.getByRole('textbox', { name: 'Domain' })
|
const bar = screen.getByRole('textbox', { name: 'Domain' })
|
||||||
@@ -398,7 +398,7 @@ describe('Rules', () => {
|
|||||||
|
|
||||||
await userEvent.click(screen.getByRole('button', { name: 'Flush' }))
|
await userEvent.click(screen.getByRole('button', { name: 'Flush' }))
|
||||||
await userEvent.click(screen.getByRole('menuitem', { name: 'Blocked zones' }))
|
await userEvent.click(screen.getByRole('menuitem', { name: 'Blocked zones' }))
|
||||||
await userEvent.click(within(await screen.findByRole('dialog')).getByRole('button', { name: 'Flush' }))
|
await userEvent.click(within(await screen.findByRole('alertdialog')).getByRole('button', { name: 'Flush' }))
|
||||||
await screen.findByText('Blocked zone was flushed successfully.')
|
await screen.findByText('Blocked zone was flushed successfully.')
|
||||||
expect(list).toHaveBeenLastCalledWith('blocked', 'T', '', undefined, 'dev.cluster.test')
|
expect(list).toHaveBeenLastCalledWith('blocked', 'T', '', undefined, 'dev.cluster.test')
|
||||||
|
|
||||||
@@ -444,7 +444,7 @@ describe('Rules', () => {
|
|||||||
const remove = vi.spyOn(zonelists, 'deleteDomain').mockResolvedValue(OK)
|
const remove = vi.spyOn(zonelists, 'deleteDomain').mockResolvedValue(OK)
|
||||||
draw(NO_MODIFY)
|
draw(NO_MODIFY)
|
||||||
await userEvent.click(await screen.findByRole('button', { name: 'Delete s.youtube.com' }))
|
await userEvent.click(await screen.findByRole('button', { name: 'Delete s.youtube.com' }))
|
||||||
const dialog = await screen.findByRole('dialog')
|
const dialog = await screen.findByRole('alertdialog')
|
||||||
expect(within(dialog).getByRole('heading', { name: 'Delete Allowed Zone' })).toBeInTheDocument()
|
expect(within(dialog).getByRole('heading', { name: 'Delete Allowed Zone' })).toBeInTheDocument()
|
||||||
expect(dialog).toHaveTextContent("Are you sure you want to delete the allowed zone 's.youtube.com'?")
|
expect(dialog).toHaveTextContent("Are you sure you want to delete the allowed zone 's.youtube.com'?")
|
||||||
await userEvent.click(within(dialog).getByRole('button', { name: 'Delete' }))
|
await userEvent.click(within(dialog).getByRole('button', { name: 'Delete' }))
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ describe('Rules: Delete with Undo', () => {
|
|||||||
expect(screen.queryByRole('dialog')).toBeNull()
|
expect(screen.queryByRole('dialog')).toBeNull()
|
||||||
expect(remove).toHaveBeenCalledWith('blocked', 'T', 'ads.test')
|
expect(remove).toHaveBeenCalledWith('blocked', 'T', 'ads.test')
|
||||||
await deleteRule('ok.test')
|
await deleteRule('ok.test')
|
||||||
const dialog = await screen.findByRole('dialog')
|
const dialog = await screen.findByRole('alertdialog')
|
||||||
expect(dialog).toHaveTextContent("Are you sure you want to delete the allowed zone 'ok.test'?")
|
expect(dialog).toHaveTextContent("Are you sure you want to delete the allowed zone 'ok.test'?")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -248,14 +248,6 @@ At 720 the lists go to one column.
|
|||||||
/* The custom range: two dates and the button, on one line. */
|
/* The custom range: two dates and the button, on one line. */
|
||||||
.ownRange { display:flex; align-items:flex-end; gap: var(--s-7); flex-wrap:wrap; margin-bottom: var(--s-9); }
|
.ownRange { display:flex; align-items:flex-end; gap: var(--s-7); flex-wrap:wrap; margin-bottom: var(--s-9); }
|
||||||
.ownRange label { display:flex; flex-direction:column; gap: var(--s-3); font-size: var(--t-3); color:var(--mute); }
|
.ownRange label { display:flex; flex-direction:column; gap: var(--s-3); font-size: var(--t-3); color:var(--mute); }
|
||||||
.ownRange input {
|
|
||||||
padding: var(--s-4) var(--s-6);
|
|
||||||
background: var(--pan2);
|
|
||||||
border: 1px solid var(--line-field);
|
|
||||||
border-radius: var(--r-2);
|
|
||||||
color: var(--ink);
|
|
||||||
font: 400 var(--t-4)/1.2 var(--font-mono);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The `Queries` legend, in HTML.
|
The `Queries` legend, in HTML.
|
||||||
@@ -296,7 +288,6 @@ It goes here and not in the notice at the top because the design rule says so:
|
|||||||
the validation error goes next to its field, and never in two places at once.
|
the validation error goes next to its field, and never in two places at once.
|
||||||
*/
|
*/
|
||||||
.badRange { color: var(--dan); font-size: var(--t-2); }
|
.badRange { color: var(--dan); font-size: var(--t-2); }
|
||||||
.ownRange input[aria-invalid] { border-color: var(--dan); }
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Beside a doughnut the legend is a small table: swatch, name, count and share, the
|
Beside a doughnut the legend is a small table: swatch, name, count and share, the
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import { useDomainAction } from '../../lib/allow-block'
|
|||||||
import { SectionHeader } from '../../ui/SectionHeader'
|
import { SectionHeader } from '../../ui/SectionHeader'
|
||||||
import { Loading } from '../../ui/Empty'
|
import { Loading } from '../../ui/Empty'
|
||||||
import { Frame, FramePanel } from '@/components/reui/frame'
|
import { Frame, FramePanel } from '@/components/reui/frame'
|
||||||
|
import { Input } from '@/components/ui/input'
|
||||||
import styles from './Dashboard.module.css'
|
import styles from './Dashboard.module.css'
|
||||||
import { Body, Panel } from '../../ui/Panel'
|
import { Body, Panel } from '../../ui/Panel'
|
||||||
import { Button } from '../../ui/Button'
|
import { Button } from '../../ui/Button'
|
||||||
@@ -346,7 +347,7 @@ export function Dashboard({
|
|||||||
<div className={styles.ownRange}>
|
<div className={styles.ownRange}>
|
||||||
<label>
|
<label>
|
||||||
Start
|
Start
|
||||||
<input
|
<Input
|
||||||
ref={startRef}
|
ref={startRef}
|
||||||
type="date"
|
type="date"
|
||||||
value={start}
|
value={start}
|
||||||
@@ -357,7 +358,7 @@ export function Dashboard({
|
|||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
End
|
End
|
||||||
<input
|
<Input
|
||||||
ref={endRef}
|
ref={endRef}
|
||||||
type="date"
|
type="date"
|
||||||
value={end}
|
value={end}
|
||||||
|
|||||||
@@ -249,7 +249,7 @@ describe('DHCP › Scopes', () => {
|
|||||||
expect(
|
expect(
|
||||||
screen.getByText("Are you sure you want to disable the DHCP scope 'Default'?"),
|
screen.getByText("Are you sure you want to disable the DHCP scope 'Default'?"),
|
||||||
).toBeInTheDocument()
|
).toBeInTheDocument()
|
||||||
await user.click(within(screen.getByRole('dialog')).getByRole('button', { name: 'Disable' }))
|
await user.click(within(screen.getByRole('alertdialog')).getByRole('button', { name: 'Disable' }))
|
||||||
|
|
||||||
expect(spy).toHaveBeenCalledWith('t', 'Default', '')
|
expect(spy).toHaveBeenCalledWith('t', 'Default', '')
|
||||||
expect(await screen.findByText('Scope Disabled!')).toBeInTheDocument()
|
expect(await screen.findByText('Scope Disabled!')).toBeInTheDocument()
|
||||||
@@ -268,7 +268,7 @@ describe('DHCP › Scopes', () => {
|
|||||||
expect(
|
expect(
|
||||||
screen.getByText("Are you sure you want to delete the DHCP scope 'Default'?"),
|
screen.getByText("Are you sure you want to delete the DHCP scope 'Default'?"),
|
||||||
).toBeInTheDocument()
|
).toBeInTheDocument()
|
||||||
await user.click(within(screen.getByRole('dialog')).getByRole('button', { name: 'Delete' }))
|
await user.click(within(screen.getByRole('alertdialog')).getByRole('button', { name: 'Delete' }))
|
||||||
|
|
||||||
expect(spy).toHaveBeenCalledWith('t', 'Default', '')
|
expect(spy).toHaveBeenCalledWith('t', 'Default', '')
|
||||||
expect(await screen.findByText('Scope Deleted!')).toBeInTheDocument()
|
expect(await screen.findByText('Scope Deleted!')).toBeInTheDocument()
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ describe('Lists embedded', () => {
|
|||||||
vi.spyOn(api, 'deleteDomain').mockResolvedValue({ kind: 'ok', data: {} })
|
vi.spyOn(api, 'deleteDomain').mockResolvedValue({ kind: 'ok', data: {} })
|
||||||
render(<Lists list="blocked" token="T" nodes={NODES} clusterInitialised embedded />)
|
render(<Lists list="blocked" token="T" nodes={NODES} clusterInitialised embedded />)
|
||||||
await userEvent.click(await screen.findByRole('button', { name: 'Delete' }))
|
await userEvent.click(await screen.findByRole('button', { name: 'Delete' }))
|
||||||
await userEvent.click(within(await screen.findByRole('dialog')).getByRole('button', { name: 'Delete' }))
|
await userEvent.click(within(await screen.findByRole('alertdialog')).getByRole('button', { name: 'Delete' }))
|
||||||
expect(list).toHaveBeenLastCalledWith('blocked', 'T', 'test', 'up', 'dev.cluster.test')
|
expect(list).toHaveBeenLastCalledWith('blocked', 'T', 'test', 'up', 'dev.cluster.test')
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ describe('Lists embedded', () => {
|
|||||||
const onChanged = vi.fn()
|
const onChanged = vi.fn()
|
||||||
render(<Lists list="blocked" token="T" embedded onNotice={onNotice} onChanged={onChanged} />)
|
render(<Lists list="blocked" token="T" embedded onNotice={onNotice} onChanged={onChanged} />)
|
||||||
await userEvent.click(await screen.findByRole('button', { name: 'Delete' }))
|
await userEvent.click(await screen.findByRole('button', { name: 'Delete' }))
|
||||||
await userEvent.click(within(await screen.findByRole('dialog')).getByRole('button', { name: 'Delete' }))
|
await userEvent.click(within(await screen.findByRole('alertdialog')).getByRole('button', { name: 'Delete' }))
|
||||||
expect(onNotice).toHaveBeenCalledWith({
|
expect(onNotice).toHaveBeenCalledWith({
|
||||||
type: 'success', title: 'Deleted!', text: "Blocked zone 'ads.test' was deleted successfully.",
|
type: 'success', title: 'Deleted!', text: "Blocked zone 'ads.test' was deleted successfully.",
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ function withNode(...nodes: ListNode[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function confirm(label: string) {
|
async function confirm(label: string) {
|
||||||
const dialog = await screen.findByRole('dialog')
|
const dialog = await screen.findByRole('alertdialog')
|
||||||
await userEvent.click(within(dialog).getByRole('button', { name: label }))
|
await userEvent.click(within(dialog).getByRole('button', { name: label }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ describe('Logs › View Logs', () => {
|
|||||||
expect(
|
expect(
|
||||||
screen.getByText("Are you sure you want to permanently delete the log file '2026-08-26'?"),
|
screen.getByText("Are you sure you want to permanently delete the log file '2026-08-26'?"),
|
||||||
).toBeInTheDocument()
|
).toBeInTheDocument()
|
||||||
await user.click(within(screen.getByRole('dialog')).getByRole('button', { name: 'Delete' }))
|
await user.click(within(screen.getByRole('alertdialog')).getByRole('button', { name: 'Delete' }))
|
||||||
|
|
||||||
expect(spy).toHaveBeenCalledWith('t', '2026-08-26', '')
|
expect(spy).toHaveBeenCalledWith('t', '2026-08-26', '')
|
||||||
expect(await screen.findByText('Log Deleted!')).toBeInTheDocument()
|
expect(await screen.findByText('Log Deleted!')).toBeInTheDocument()
|
||||||
@@ -177,7 +177,7 @@ describe('Logs › View Logs', () => {
|
|||||||
screen.getByText('Are you sure you want to permanently delete all log files?'),
|
screen.getByText('Are you sure you want to permanently delete all log files?'),
|
||||||
).toBeInTheDocument()
|
).toBeInTheDocument()
|
||||||
await user.click(
|
await user.click(
|
||||||
within(screen.getByRole('dialog')).getByRole('button', { name: 'Delete All Logs' }),
|
within(screen.getByRole('alertdialog')).getByRole('button', { name: 'Delete All Logs' }),
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(spy).toHaveBeenCalledWith('t', '')
|
expect(spy).toHaveBeenCalledWith('t', '')
|
||||||
@@ -197,7 +197,7 @@ describe('Logs › View Logs', () => {
|
|||||||
screen.getByText('Are you sure you want to permanently delete all stats files?'),
|
screen.getByText('Are you sure you want to permanently delete all stats files?'),
|
||||||
).toBeInTheDocument()
|
).toBeInTheDocument()
|
||||||
await user.click(
|
await user.click(
|
||||||
within(screen.getByRole('dialog')).getByRole('button', { name: 'Delete All Stats' }),
|
within(screen.getByRole('alertdialog')).getByRole('button', { name: 'Delete All Stats' }),
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(spy).toHaveBeenCalledWith('t')
|
expect(spy).toHaveBeenCalledWith('t')
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ describe('zone list', () => {
|
|||||||
|
|
||||||
await user.click(screen.getByLabelText('Select casa.test'))
|
await user.click(screen.getByLabelText('Select casa.test'))
|
||||||
await user.click(screen.getByRole('button', { name: 'Delete Zones' }))
|
await user.click(screen.getByRole('button', { name: 'Delete Zones' }))
|
||||||
await user.click(within(screen.getByRole('dialog')).getByRole('button', { name: 'Delete' }))
|
await user.click(within(screen.getByRole('alertdialog')).getByRole('button', { name: 'Delete' }))
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
const call = spy.mock.calls.find((c) => c[0] === 'zones/delete')
|
const call = spy.mock.calls.find((c) => c[0] === 'zones/delete')
|
||||||
@@ -166,7 +166,7 @@ describe('zone list', () => {
|
|||||||
|
|
||||||
await user.click(screen.getByLabelText('Select casa.test'))
|
await user.click(screen.getByLabelText('Select casa.test'))
|
||||||
await user.click(screen.getByRole('button', { name: 'Delete Zones' }))
|
await user.click(screen.getByRole('button', { name: 'Delete Zones' }))
|
||||||
await user.click(within(screen.getByRole('dialog')).getByRole('button', { name: 'Delete' }))
|
await user.click(within(screen.getByRole('alertdialog')).getByRole('button', { name: 'Delete' }))
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
await screen.findByText(
|
await screen.findByText(
|
||||||
@@ -227,7 +227,7 @@ describe('records of a zone', () => {
|
|||||||
await user.click(await screen.findByRole('menuitem', { name: 'Delete Record' }))
|
await user.click(await screen.findByRole('menuitem', { name: 'Delete Record' }))
|
||||||
expect(await screen.findByText("Are you sure to permanently delete the A record 'www.casa.test'?")).toBeTruthy()
|
expect(await screen.findByText("Are you sure to permanently delete the A record 'www.casa.test'?")).toBeTruthy()
|
||||||
|
|
||||||
await user.click(within(screen.getByRole('dialog')).getByRole('button', { name: 'Delete' }))
|
await user.click(within(screen.getByRole('alertdialog')).getByRole('button', { name: 'Delete' }))
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
const call = spy.mock.calls.find((c) => String(c[0]).startsWith('zones/records/delete'))
|
const call = spy.mock.calls.find((c) => String(c[0]).startsWith('zones/records/delete'))
|
||||||
expect(call![1]?.body).toMatchObject({
|
expect(call![1]?.body).toMatchObject({
|
||||||
|
|||||||
@@ -432,7 +432,7 @@ describe('SessionProvider', () => {
|
|||||||
const user = await openMenu()
|
const user = await openMenu()
|
||||||
await user.click(await screen.findByRole('menuitem', { name: 'Disable Update Notification' }))
|
await user.click(await screen.findByRole('menuitem', { name: 'Disable Update Notification' }))
|
||||||
|
|
||||||
const dialog = await screen.findByRole('dialog')
|
const dialog = await screen.findByRole('alertdialog')
|
||||||
expect(dialog).toHaveTextContent(
|
expect(dialog).toHaveTextContent(
|
||||||
'Disabling update notification will prevent the Web Console from showing new update notification when you login.',
|
'Disabling update notification will prevent the Web Console from showing new update notification when you login.',
|
||||||
)
|
)
|
||||||
|
|||||||
+7
-4
@@ -1,6 +1,7 @@
|
|||||||
import { useEffect, useRef, type ReactNode } from 'react'
|
import { useEffect, useRef, type ReactNode } from 'react'
|
||||||
import { Icon, type IconName } from './Icon'
|
import { Icon, type IconName } from './Icon'
|
||||||
import { Alert as ReuiAlert } from '@/components/reui/alert'
|
import { Alert as ReuiAlert } from '@/components/reui/alert'
|
||||||
|
import { Button as ReuiButton } from '@/components/ui/button'
|
||||||
|
|
||||||
/*
|
/*
|
||||||
A replica of the old console's `showAlert` (common.js:202-217): a type, a bold
|
A replica of the old console's `showAlert` (common.js:202-217): a type, a bold
|
||||||
@@ -79,14 +80,16 @@ export function Alert({
|
|||||||
<b className="font-medium">{title}</b> {children}
|
<b className="font-medium">{title}</b> {children}
|
||||||
</div>
|
</div>
|
||||||
{onDismiss && (
|
{onDismiss && (
|
||||||
<button
|
<ReuiButton
|
||||||
type="button"
|
type="button"
|
||||||
className="absolute top-2 right-2 inline-flex size-6 border-0 bg-transparent items-center justify-center rounded-md text-muted-foreground transition-colors outline-none hover:bg-muted hover:text-foreground focus-visible:ring-3 focus-visible:ring-ring/50"
|
variant="ghost"
|
||||||
|
size="icon-xs"
|
||||||
|
className="absolute top-2 right-2 text-muted-foreground"
|
||||||
aria-label="Close"
|
aria-label="Close"
|
||||||
onClick={onDismiss}
|
onClick={onDismiss}
|
||||||
>
|
>
|
||||||
<Icon name="close" size={14} />
|
<Icon name="close" size={14} className="size-3.5" />
|
||||||
</button>
|
</ReuiButton>
|
||||||
)}
|
)}
|
||||||
</ReuiAlert>
|
</ReuiAlert>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,4 +1,14 @@
|
|||||||
import { useState, type ReactNode } from 'react'
|
import { useState, type ReactNode } from 'react'
|
||||||
|
import {
|
||||||
|
AlertDialog,
|
||||||
|
AlertDialogAction,
|
||||||
|
AlertDialogCancel,
|
||||||
|
AlertDialogContent,
|
||||||
|
AlertDialogDescription,
|
||||||
|
AlertDialogFooter,
|
||||||
|
AlertDialogHeader,
|
||||||
|
AlertDialogTitle,
|
||||||
|
} from '@/components/ui/alert-dialog'
|
||||||
import { Button } from './Button'
|
import { Button } from './Button'
|
||||||
import { Check } from './Check'
|
import { Check } from './Check'
|
||||||
import { Dialog } from './Dialog'
|
import { Dialog } from './Dialog'
|
||||||
@@ -64,6 +74,32 @@ export function Confirm({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* A destructive confirm is ReUI's `AlertDialog`: it takes no outside click and
|
||||||
|
cannot be dismissed while the request runs. The one that only converts or
|
||||||
|
enables keeps the plain dialog. */
|
||||||
|
if (variant === 'danger') {
|
||||||
|
const pending = busy || inProgress
|
||||||
|
return (
|
||||||
|
<AlertDialog open={open} onOpenChange={(o) => !o && !pending && onClose()}>
|
||||||
|
<AlertDialogContent>
|
||||||
|
<AlertDialogHeader>
|
||||||
|
<AlertDialogTitle>{title}</AlertDialogTitle>
|
||||||
|
{/* `pre-wrap`: the Zones confirmations are multi-line. */}
|
||||||
|
<AlertDialogDescription render={<div />} style={{ whiteSpace: 'pre-wrap' }}>
|
||||||
|
{body}
|
||||||
|
</AlertDialogDescription>
|
||||||
|
</AlertDialogHeader>
|
||||||
|
<AlertDialogFooter>
|
||||||
|
<AlertDialogCancel disabled={pending}>Cancel</AlertDialogCancel>
|
||||||
|
<AlertDialogAction variant="destructive" disabled={pending} onClick={confirm}>
|
||||||
|
{label}
|
||||||
|
</AlertDialogAction>
|
||||||
|
</AlertDialogFooter>
|
||||||
|
</AlertDialogContent>
|
||||||
|
</AlertDialog>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
open={open}
|
open={open}
|
||||||
|
|||||||
+2
-2
@@ -31,13 +31,13 @@ export function KpiStrip({ items, label }: { items: Kpi[]; label: string }) {
|
|||||||
className="flex min-w-0 items-center gap-3 border-r border-b border-border px-[18px] py-3.5 last:border-r-0"
|
className="flex min-w-0 items-center gap-3 border-r border-b border-border px-[18px] py-3.5 last:border-r-0"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className={`flex size-10 flex-none items-center justify-center rounded-lg border border-border bg-muted shadow-sm ${k.tone ? TEXT[k.tone] : 'text-foreground'}`}
|
className={`flex size-10 flex-none items-center justify-center rounded-lg border border-border bg-muted ${k.tone ? TEXT[k.tone] : 'text-foreground'}`}
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
>
|
>
|
||||||
<Icon name={k.icon} size={18} />
|
<Icon name={k.icon} size={18} />
|
||||||
</span>
|
</span>
|
||||||
<div className="flex min-w-0 flex-col gap-1">
|
<div className="flex min-w-0 flex-col gap-1">
|
||||||
<div className="text-[13px] text-muted-foreground">{k.label}</div>
|
<div className="text-sm text-muted-foreground">{k.label}</div>
|
||||||
<div className="flex flex-wrap items-baseline gap-x-2 gap-y-1">
|
<div className="flex flex-wrap items-baseline gap-x-2 gap-y-1">
|
||||||
<span className={`text-2xl font-semibold tabular-nums ${k.tone ? TEXT[k.tone] : ''}`}>{k.value}</span>
|
<span className={`text-2xl font-semibold tabular-nums ${k.tone ? TEXT[k.tone] : ''}`}>{k.value}</span>
|
||||||
{k.hint != null && <Badge variant={k.tone ? BADGE[k.tone] : 'outline'}>{k.hint}</Badge>}
|
{k.hint != null && <Badge variant={k.tone ? BADGE[k.tone] : 'outline'}>{k.hint}</Badge>}
|
||||||
|
|||||||
Reference in new issue
Block a user