Skip to content

Commit

Permalink
refactor: improve multiselect behaviours
Browse files Browse the repository at this point in the history
  • Loading branch information
alexfreska committed Dec 10, 2024
1 parent f3d2a95 commit c90882c
Show file tree
Hide file tree
Showing 33 changed files with 845 additions and 520 deletions.
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
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
20 changes: 8 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,8 @@ 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()
}
rows.at(0).click()
rows.at(-1).click({ modifiers: ['Shift'] })

const menu = page.getByLabel('host multi-select menu')
const dialog = page.getByRole('dialog')
Expand All @@ -57,9 +56,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'] })

// Remove selected hosts from the allowlist.
await menu.getByLabel('remove host public keys from allowlist').click()
Expand All @@ -81,9 +79,8 @@ 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()
}
rows[0].click()
rows[rows.length - 1].click({ modifiers: ['Shift'] })

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

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

// 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
2 changes: 1 addition & 1 deletion apps/renterd/contexts/filesFlat/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ function useFilesFlatMain() {
return _datasetPage.map((datum) => {
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/hosts/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ function useHostsMain() {
...datum,
onClick: (e: React.MouseEvent<HTMLTableRowElement>) =>
multiSelect.onSelect(datum.id, e),
isSelected: !!multiSelect.selectionMap[datum.id],
isSelected: !!multiSelect.selection[datum.id],
}
})
}, [dataset, multiSelect])
Expand Down
2 changes: 1 addition & 1 deletion apps/renterd/contexts/keys/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ function useKeysMain() {
...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
5 changes: 5 additions & 0 deletions libs/design-system/src/components/Table/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,11 @@ export function Table<
<Panel>
<table
data-testid={testId}
onMouseDown={(e) => {
if (e.shiftKey) {
e.preventDefault()
}
}}
data-loading={show === 'skeleton'}
className="relative z-10 table-auto border-collapse w-full"
>
Expand Down
Loading

0 comments on commit c90882c

Please sign in to comment.