diff --git a/res/css/components/views/settings/devices/_FilteredDeviceList.pcss b/res/css/components/views/settings/devices/_FilteredDeviceList.pcss
index d60c9628fb..d34270e8ad 100644
--- a/res/css/components/views/settings/devices/_FilteredDeviceList.pcss
+++ b/res/css/components/views/settings/devices/_FilteredDeviceList.pcss
@@ -45,4 +45,8 @@ limitations under the License.
.mx_FilteredDeviceList_headerButton {
flex-shrink: 0;
+ // override inline button styling
+ display: flex !important;
+ flex-direction: row;
+ gap: $spacing-8;
}
diff --git a/res/css/views/elements/_AccessibleButton.pcss b/res/css/views/elements/_AccessibleButton.pcss
index f891e810be..dd3d0945e0 100644
--- a/res/css/views/elements/_AccessibleButton.pcss
+++ b/res/css/views/elements/_AccessibleButton.pcss
@@ -25,6 +25,8 @@ limitations under the License.
&.mx_AccessibleButton_kind_primary_sm,
&.mx_AccessibleButton_kind_link,
&.mx_AccessibleButton_kind_link_inline,
+ &.mx_AccessibleButton_kind_danger_inline,
+ &.mx_AccessibleButton_kind_content_inline,
&.mx_AccessibleButton_kind_link_sm {
opacity: 0.4;
}
diff --git a/src/components/views/settings/devices/FilteredDeviceList.tsx b/src/components/views/settings/devices/FilteredDeviceList.tsx
index 31ed9f2dca..9bc216a086 100644
--- a/src/components/views/settings/devices/FilteredDeviceList.tsx
+++ b/src/components/views/settings/devices/FilteredDeviceList.tsx
@@ -37,6 +37,7 @@ import {
} from './types';
import { DevicesState } from './useOwnDevices';
import FilteredDeviceListHeader from './FilteredDeviceListHeader';
+import Spinner from '../../elements/Spinner';
interface Props {
devices: DevicesDictionary;
@@ -183,6 +184,7 @@ const DeviceListItem: React.FC<{
onClick={onDeviceExpandToggle}
device={device}
>
+ { isSigningOut && }
onSignOutDevices(selectedDeviceIds)}
className='mx_FilteredDeviceList_headerButton'
>
+ { isSigningOut && }
{ _t('Sign out') }
setSelectedDeviceIds([])}
className='mx_FilteredDeviceList_headerButton'
>
diff --git a/test/components/views/settings/tabs/user/SessionManagerTab-test.tsx b/test/components/views/settings/tabs/user/SessionManagerTab-test.tsx
index ed10e64336..5bcb6cc36c 100644
--- a/test/components/views/settings/tabs/user/SessionManagerTab-test.tsx
+++ b/test/components/views/settings/tabs/user/SessionManagerTab-test.tsx
@@ -69,7 +69,7 @@ describe('', () => {
};
const alicesInactiveDevice = {
- device_id: 'alices_older_mobile_device',
+ device_id: 'alices_older_inactive_mobile_device',
last_seen_ts: Date.now() - (INACTIVE_DEVICE_AGE_MS + 1000),
};
@@ -105,7 +105,7 @@ describe('', () => {
const toggleDeviceDetails = (
getByTestId: ReturnType['getByTestId'],
deviceId: ExtendedDevice['device_id'],
- ) => {
+ ): void => {
// open device detail
const tile = getByTestId(`device-tile-${deviceId}`);
const toggle = tile.querySelector('[aria-label="Toggle device details"]') as Element;
@@ -115,11 +115,18 @@ describe('', () => {
const toggleDeviceSelection = (
getByTestId: ReturnType['getByTestId'],
deviceId: ExtendedDevice['device_id'],
- ) => {
+ ): void => {
const checkbox = getByTestId(`device-tile-checkbox-${deviceId}`);
fireEvent.click(checkbox);
};
+ const getDeviceTile = (
+ getByTestId: ReturnType['getByTestId'],
+ deviceId: ExtendedDevice['device_id'],
+ ): HTMLElement => {
+ return getByTestId(`device-tile-${deviceId}`);
+ };
+
const setFilter = async (
container: HTMLElement,
option: DeviceSecurityVariation | string,
@@ -749,6 +756,7 @@ describe('', () => {
it('deletes multiple devices', async () => {
mockClient.getDevices.mockResolvedValue({ devices: [
alicesDevice, alicesMobileDevice, alicesOlderMobileDevice,
+ alicesInactiveDevice,
] });
mockClient.deleteMultipleDevices.mockResolvedValue({});
@@ -763,6 +771,24 @@ describe('', () => {
fireEvent.click(getByTestId('sign-out-selection-cta'));
+ // buttons disabled in list header
+ expect(getByTestId('sign-out-selection-cta').getAttribute('aria-disabled')).toBeTruthy();
+ expect(getByTestId('cancel-selection-cta').getAttribute('aria-disabled')).toBeTruthy();
+ // spinner rendered in list header
+ expect(getByTestId('sign-out-selection-cta').querySelector('.mx_Spinner')).toBeTruthy();
+
+ // spinners on signing out devices
+ expect(getDeviceTile(
+ getByTestId, alicesMobileDevice.device_id,
+ ).querySelector('.mx_Spinner')).toBeTruthy();
+ expect(getDeviceTile(
+ getByTestId, alicesOlderMobileDevice.device_id,
+ ).querySelector('.mx_Spinner')).toBeTruthy();
+ // no spinner for device that is not signing out
+ expect(getDeviceTile(
+ getByTestId, alicesInactiveDevice.device_id,
+ ).querySelector('.mx_Spinner')).toBeFalsy();
+
// delete called with both ids
expect(mockClient.deleteMultipleDevices).toHaveBeenCalledWith(
[