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

refactor: improve multiselect behaviours #844

Merged
merged 1 commit into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/flat-days-camp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@siafoundation/design-system': minor
---

Renamed selectionMap to selection in the useMultiSelect hook API.
7 changes: 7 additions & 0 deletions .changeset/real-paws-accept.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'hostd': minor
'renterd': minor
'walletd': minor
---

Multi-select now supports single select, toggle select, and range selection interactions, with click, ctrl-click, and shift-click.
5 changes: 5 additions & 0 deletions .changeset/wicked-radios-tie.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@siafoundation/design-system': minor
---

Table now prevents default on any mouse down with shift held. This ensures the user does not highlight text while shift selecting a range of rows.
5 changes: 2 additions & 3 deletions apps/hostd-e2e/src/specs/contracts.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,8 @@ test.afterEach(async () => {
test('contracts bulk integrity check', async ({ page }) => {
await navigateToContracts(page)
const rows = await getContractRowsAll(page)
for (const row of rows) {
await row.click()
}
rows.at(0).click()
rows.at(-1).click({ modifiers: ['Shift'] })

const menu = page.getByLabel('contract multi-select menu')

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ export function ContractsBulkIntegrityCheck() {
const integrityCheck = useContractsIntegrityCheck()

const ids = useMemo(
() => Object.entries(multiSelect.selectionMap).map(([_, item]) => item.id),
[multiSelect.selectionMap]
() => Object.entries(multiSelect.selection).map(([_, item]) => item.id),
[multiSelect.selection]
)
const checkAll = useCallback(async () => {
await handleBatchOperation(
Expand Down
2 changes: 1 addition & 1 deletion apps/hostd/contexts/contracts/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ function useContractsMain() {
...datum,
onClick: (e: React.MouseEvent<HTMLTableRowElement>) =>
multiSelect.onSelect(datum.id, e),
isSelected: !!multiSelect.selectionMap[datum.id],
isSelected: !!multiSelect.selection[datum.id],
}
})
}, [_datasetPage, multiSelect])
Expand Down
25 changes: 10 additions & 15 deletions apps/renterd-e2e/src/specs/contracts.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,8 @@ test('contracts prunable size', async ({ page }) => {
test('contracts bulk delete', async ({ page }) => {
await navigateToContracts({ page })
const rows = await getContractRowsAll(page)
for (const row of rows) {
await row.click()
}
rows.at(0).click()
rows.at(-1).click({ modifiers: ['Shift'] })

// Delete selected contracts.
const menu = page.getByLabel('contract multi-select menu')
Expand All @@ -82,9 +81,8 @@ test('contracts bulk delete', async ({ page }) => {
test('contracts bulk allowlist', async ({ page }) => {
await navigateToContracts({ page })
const rows = await getContractRowsAll(page)
for (const row of rows) {
await row.click()
}
rows.at(0).click()
rows.at(-1).click({ modifiers: ['Shift'] })

const menu = page.getByLabel('contract multi-select menu')
const dialog = page.getByRole('dialog')
Expand All @@ -101,9 +99,8 @@ test('contracts bulk allowlist', async ({ page }) => {
).toHaveCount(3)
await dialog.getByLabel('close').click()

for (const row of rows) {
await row.click()
}
rows.at(0).click()
rows.at(-1).click({ modifiers: ['Shift'] })

// Remove selected contract hosts from the allowlist.
await menu.getByLabel('remove host public keys from allowlist').click()
Expand All @@ -118,9 +115,8 @@ test('contracts bulk allowlist', async ({ page }) => {
test('contracts bulk blocklist', async ({ page }) => {
await navigateToContracts({ page })
const rows = await getContractRowsAll(page)
for (const row of rows) {
await row.click()
}
rows.at(0).click()
rows.at(-1).click({ modifiers: ['Shift'] })

const menu = page.getByLabel('contract multi-select menu')
const dialog = page.getByRole('dialog')
Expand All @@ -137,9 +133,8 @@ test('contracts bulk blocklist', async ({ page }) => {
await expect(dialog.getByText('The allowlist is empty')).toBeVisible()
await dialog.getByLabel('close').click()

for (const row of rows) {
await row.click()
}
rows.at(0).click()
rows.at(-1).click({ modifiers: ['Shift'] })

// Remove selected contract hosts from the blocklist.
await menu.getByLabel('remove host addresses from blocklist').click()
Expand Down
12 changes: 6 additions & 6 deletions apps/renterd-e2e/src/specs/files.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -242,14 +242,14 @@ test('bulk delete across nested directories', async ({ page }) => {

// Select entire dir1.
const dir1 = await getFileRowById(page, 'bucket1/dir1/')
await dir1.click()
await dir1.click({ modifiers: ['ControlOrMeta'] })
await openDirectory(page, 'bucket1/dir2/')

// Select file3 and file4.
const file3 = await getFileRowById(page, 'bucket1/dir2/file3.txt')
await file3.click()
await file3.click({ modifiers: ['ControlOrMeta'] })
const file4 = await getFileRowById(page, 'bucket1/dir2/file4.txt')
await file4.click()
await file4.click({ modifiers: ['ControlOrMeta'] })
const menu = page.getByLabel('file multi-select menu')

// Delete selected files.
Expand Down Expand Up @@ -289,12 +289,12 @@ test('bulk delete using the all files explorer mode', async ({ page }) => {

// Select entire dir1.
const dir1 = await getFileRowById(page, 'bucket1/dir1/')
await dir1.click()
await dir1.click({ modifiers: ['ControlOrMeta'] })
// Select file3 and file4.
const file3 = await getFileRowById(page, 'bucket1/dir2/file3.txt')
await file3.click()
await file3.click({ modifiers: ['ControlOrMeta'] })
const file4 = await getFileRowById(page, 'bucket1/dir2/file4.txt')
await file4.click()
await file4.click({ modifiers: ['ControlOrMeta'] })
const menu = page.getByLabel('file multi-select menu')

// Delete selected files.
Expand Down
6 changes: 3 additions & 3 deletions apps/renterd-e2e/src/specs/filesMove.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ test('move two files by selecting and dragging from one directory out to another
const file3 = await getFileRowById(page, 'bucket1/dir2/file3.txt', true)
await file3.click()
const dir3 = await getFileRowById(page, 'bucket1/dir2/dir3/', true)
await dir3.click()
await dir3.click({ modifiers: ['ControlOrMeta'] })

// Move all selected files by dragging one of them.
await moveMouseOver(page, file3)
Expand Down Expand Up @@ -108,7 +108,7 @@ test('move a file via drag and drop while leaving a separate set of selected fil
const file3 = await getFileRowById(page, 'bucket1/dir2/file3.txt', true)
await file3.click()
const file4 = await getFileRowById(page, 'bucket1/dir2/file4.txt', true)
await file4.click()
await file4.click({ modifiers: ['ControlOrMeta'] })

// Move file5 which is not in the selection.
const file5 = await getFileRowById(page, 'bucket1/dir2/file5.txt', true)
Expand Down Expand Up @@ -165,7 +165,7 @@ test('move files by selecting and using the docked menu bulk action', async ({
const file3 = await getFileRowById(page, 'bucket1/dir2/file3.txt', true)
await file3.click()
const dir3 = await getFileRowById(page, 'bucket1/dir2/dir3/', true)
await dir3.click()
await dir3.click({ modifiers: ['ControlOrMeta'] })

await navigateToParentDirectory(page)

Expand Down
22 changes: 10 additions & 12 deletions apps/renterd-e2e/src/specs/hosts.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ test('hosts explorer shows all hosts', async ({ page }) => {
test('hosts bulk allowlist', async ({ page }) => {
await navigateToHosts({ page })
const rows = await getHostRowsAll(page)
for (const row of rows) {
await row.click()
}
// Range select the rows, add position as default location is a context menu button.
rows.at(0).click()
rows.at(-1).click({ modifiers: ['Shift'], position: { x: 5, y: 5 } })

const menu = page.getByLabel('host multi-select menu')
const dialog = page.getByRole('dialog')
Expand All @@ -57,9 +57,8 @@ test('hosts bulk allowlist', async ({ page }) => {
getHostRows(page).getByTestId('allow').getByTestId('allowed')
).toHaveCount(3)

for (const row of rows) {
await row.click()
}
rows.at(0).click()
rows.at(-1).click({ modifiers: ['Shift'], position: { x: 5, y: 5 } })

// Remove selected hosts from the allowlist.
await menu.getByLabel('remove host public keys from allowlist').click()
Expand All @@ -81,9 +80,9 @@ test('hosts bulk allowlist', async ({ page }) => {
test('hosts bulk blocklist', async ({ page }) => {
await navigateToHosts({ page })
const rows = await getHostRowsAll(page)
for (const row of rows) {
await row.click()
}
// Range select the rows, add position as default location is a context menu button.
rows.at(0).click()
rows.at(-1).click({ modifiers: ['Shift'], position: { x: 5, y: 5 } })

const menu = page.getByLabel('host multi-select menu')
const dialog = page.getByRole('dialog')
Expand All @@ -106,9 +105,8 @@ test('hosts bulk blocklist', async ({ page }) => {
getHostRows(page).getByTestId('allow').getByTestId('allowed')
).toHaveCount(0)

for (const row of rows) {
await row.click()
}
rows.at(0).click()
rows.at(-1).click({ modifiers: ['Shift'], position: { x: 5, y: 5 } })

// Remove selected hosts from the blocklist.
await menu.getByLabel('remove host addresses from blocklist').click()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ export function ContractsAddAllowlist() {

const publicKeys = useMemo(
() =>
Object.entries(multiSelect.selectionMap).map(([_, item]) => item.hostKey),
[multiSelect.selectionMap]
Object.entries(multiSelect.selection).map(([_, item]) => item.hostKey),
[multiSelect.selection]
)

return <BulkAddAllowlist multiSelect={multiSelect} publicKeys={publicKeys} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@ export function ContractsAddBlocklist() {
const { multiSelect } = useContracts()

const hostAddresses = useMemo(
() =>
Object.entries(multiSelect.selectionMap).map(([_, item]) => item.hostIp),
[multiSelect.selectionMap]
() => Object.entries(multiSelect.selection).map(([_, item]) => item.hostIp),
[multiSelect.selection]
)
return (
<BulkAddBlocklist multiSelect={multiSelect} hostAddresses={hostAddresses} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ export function ContractsBulkDelete() {
const { multiSelect } = useContracts()

const ids = useMemo(
() => Object.entries(multiSelect.selectionMap).map(([_, item]) => item.id),
[multiSelect.selectionMap]
() => Object.entries(multiSelect.selection).map(([_, item]) => item.id),
[multiSelect.selection]
)
const { openConfirmDialog } = useDialog()
const deleteContract = useContractDelete()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ export function ContractsRemoveAllowlist() {

const publicKeys = useMemo(
() =>
Object.entries(multiSelect.selectionMap).map(([_, item]) => item.hostKey),
[multiSelect.selectionMap]
Object.entries(multiSelect.selection).map(([_, item]) => item.hostKey),
[multiSelect.selection]
)

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@ export function ContractsRemoveBlocklist() {
const { multiSelect } = useContracts()

const hostAddresses = useMemo(
() =>
Object.entries(multiSelect.selectionMap).map(([_, item]) => item.hostIp),
[multiSelect.selectionMap]
() => Object.entries(multiSelect.selection).map(([_, item]) => item.hostIp),
[multiSelect.selection]
)

return (
Expand Down
4 changes: 2 additions & 2 deletions apps/renterd/components/Files/bulkActions/FilesBulkDelete.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ export function FilesBulkDelete({
}) {
const filesToDelete = useMemo(
() =>
Object.entries(multiSelect.selectionMap).map(([_, item]) => ({
Object.entries(multiSelect.selection).map(([_, item]) => ({
bucket: item.bucket.name,
prefix: item.key,
})),
[multiSelect.selectionMap]
[multiSelect.selection]
)
const { openConfirmDialog } = useDialog()
const objectsRemove = useObjectsRemove()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@ export function HostsAddAllowlist() {

const publicKeys = useMemo(
() =>
Object.entries(multiSelect.selectionMap).map(
([_, item]) => item.publicKey
),
[multiSelect.selectionMap]
Object.entries(multiSelect.selection).map(([_, item]) => item.publicKey),
[multiSelect.selection]
)

return <BulkAddAllowlist multiSelect={multiSelect} publicKeys={publicKeys} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@ export function HostsAddBlocklist() {

const hostAddresses = useMemo(
() =>
Object.entries(multiSelect.selectionMap).map(
([_, item]) => item.netAddress
),
[multiSelect.selectionMap]
Object.entries(multiSelect.selection).map(([_, item]) => item.netAddress),
[multiSelect.selection]
)
return (
<BulkAddBlocklist multiSelect={multiSelect} hostAddresses={hostAddresses} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@ export function HostsRemoveAllowlist() {

const publicKeys = useMemo(
() =>
Object.entries(multiSelect.selectionMap).map(
([_, item]) => item.publicKey
),
[multiSelect.selectionMap]
Object.entries(multiSelect.selection).map(([_, item]) => item.publicKey),
[multiSelect.selection]
)

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@ export function HostsRemoveBlocklist() {

const hostAddresses = useMemo(
() =>
Object.entries(multiSelect.selectionMap).map(
([_, item]) => item.netAddress
),
[multiSelect.selectionMap]
Object.entries(multiSelect.selection).map(([_, item]) => item.netAddress),
[multiSelect.selection]
)

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,8 @@ export function HostsResetLostSectorCount() {

const publicKeys = useMemo(
() =>
Object.entries(multiSelect.selectionMap).map(
([_, item]) => item.publicKey
),
[multiSelect.selectionMap]
Object.entries(multiSelect.selection).map(([_, item]) => item.publicKey),
[multiSelect.selection]
)
const resetAll = useCallback(async () => {
await handleBatchOperation(
Expand Down
4 changes: 2 additions & 2 deletions apps/renterd/components/Keys/KeysBulkMenu/KeysBulkDelete.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ export function KeysBulkDelete() {
const { multiSelect } = useKeys()

const keys = useMemo(
() => Object.entries(multiSelect.selectionMap).map(([_, item]) => item.key),
[multiSelect.selectionMap]
() => Object.entries(multiSelect.selection).map(([_, item]) => item.key),
[multiSelect.selection]
)
const { openConfirmDialog } = useDialog()
const settingsS3 = useSettingsS3()
Expand Down
2 changes: 1 addition & 1 deletion apps/renterd/contexts/contracts/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ function useContractsMain() {
...datum,
onClick: (e: React.MouseEvent<HTMLTableRowElement>) =>
multiSelect.onSelect(datum.id, e),
isSelected: !!multiSelect.selectionMap[datum.id],
isSelected: !!multiSelect.selection[datum.id],
}
})
}, [_datasetPage, multiSelect])
Expand Down
2 changes: 1 addition & 1 deletion apps/renterd/contexts/filesDirectory/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ function useFilesDirectoryMain() {
}
return {
...datum,
isSelected: !!multiSelect.selectionMap[datum.id],
isSelected: !!multiSelect.selection[datum.id],
onClick: (e: MouseEvent<HTMLTableRowElement>) =>
multiSelect.onSelect(datum.id, e),
}
Expand Down
2 changes: 1 addition & 1 deletion apps/renterd/contexts/filesDirectory/move.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ export function useMove({
const id = String(e.active.id)
if (multiSelect.selectedIds.includes(id)) {
setDraggingObjects(
Object.entries(multiSelect.selectionMap).map(([, obj]) => obj)
Object.entries(multiSelect.selection).map(([, obj]) => obj)
)
} else {
const ob = dataset?.find((d) => d.id === e.active.id)
Expand Down
Loading
Loading