device manager - add spinners while devices are signing out (#9433)

pull/28788/head^2
Kerry 2022-10-17 17:14:49 +02:00 committed by GitHub
parent 72d7939afc
commit 877c95df8f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 42 additions and 3 deletions

View File

@ -45,4 +45,8 @@ limitations under the License.
.mx_FilteredDeviceList_headerButton { .mx_FilteredDeviceList_headerButton {
flex-shrink: 0; flex-shrink: 0;
// override inline button styling
display: flex !important;
flex-direction: row;
gap: $spacing-8;
} }

View File

@ -25,6 +25,8 @@ limitations under the License.
&.mx_AccessibleButton_kind_primary_sm, &.mx_AccessibleButton_kind_primary_sm,
&.mx_AccessibleButton_kind_link, &.mx_AccessibleButton_kind_link,
&.mx_AccessibleButton_kind_link_inline, &.mx_AccessibleButton_kind_link_inline,
&.mx_AccessibleButton_kind_danger_inline,
&.mx_AccessibleButton_kind_content_inline,
&.mx_AccessibleButton_kind_link_sm { &.mx_AccessibleButton_kind_link_sm {
opacity: 0.4; opacity: 0.4;
} }

View File

@ -37,6 +37,7 @@ import {
} from './types'; } from './types';
import { DevicesState } from './useOwnDevices'; import { DevicesState } from './useOwnDevices';
import FilteredDeviceListHeader from './FilteredDeviceListHeader'; import FilteredDeviceListHeader from './FilteredDeviceListHeader';
import Spinner from '../../elements/Spinner';
interface Props { interface Props {
devices: DevicesDictionary; devices: DevicesDictionary;
@ -183,6 +184,7 @@ const DeviceListItem: React.FC<{
onClick={onDeviceExpandToggle} onClick={onDeviceExpandToggle}
device={device} device={device}
> >
{ isSigningOut && <Spinner w={16} h={16} /> }
<DeviceExpandDetailsButton <DeviceExpandDetailsButton
isExpanded={isExpanded} isExpanded={isExpanded}
onClick={onDeviceExpandToggle} onClick={onDeviceExpandToggle}
@ -276,6 +278,8 @@ export const FilteredDeviceList =
} }
}; };
const isSigningOut = !!signingOutDeviceIds.length;
return <div className='mx_FilteredDeviceList' ref={ref}> return <div className='mx_FilteredDeviceList' ref={ref}>
<FilteredDeviceListHeader <FilteredDeviceListHeader
selectedDeviceCount={selectedDeviceIds.length} selectedDeviceCount={selectedDeviceIds.length}
@ -287,14 +291,17 @@ export const FilteredDeviceList =
<AccessibleButton <AccessibleButton
data-testid='sign-out-selection-cta' data-testid='sign-out-selection-cta'
kind='danger_inline' kind='danger_inline'
disabled={isSigningOut}
onClick={() => onSignOutDevices(selectedDeviceIds)} onClick={() => onSignOutDevices(selectedDeviceIds)}
className='mx_FilteredDeviceList_headerButton' className='mx_FilteredDeviceList_headerButton'
> >
{ isSigningOut && <Spinner w={16} h={16} /> }
{ _t('Sign out') } { _t('Sign out') }
</AccessibleButton> </AccessibleButton>
<AccessibleButton <AccessibleButton
data-testid='cancel-selection-cta' data-testid='cancel-selection-cta'
kind='content_inline' kind='content_inline'
disabled={isSigningOut}
onClick={() => setSelectedDeviceIds([])} onClick={() => setSelectedDeviceIds([])}
className='mx_FilteredDeviceList_headerButton' className='mx_FilteredDeviceList_headerButton'
> >

View File

@ -69,7 +69,7 @@ describe('<SessionManagerTab />', () => {
}; };
const alicesInactiveDevice = { 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), last_seen_ts: Date.now() - (INACTIVE_DEVICE_AGE_MS + 1000),
}; };
@ -105,7 +105,7 @@ describe('<SessionManagerTab />', () => {
const toggleDeviceDetails = ( const toggleDeviceDetails = (
getByTestId: ReturnType<typeof render>['getByTestId'], getByTestId: ReturnType<typeof render>['getByTestId'],
deviceId: ExtendedDevice['device_id'], deviceId: ExtendedDevice['device_id'],
) => { ): void => {
// open device detail // open device detail
const tile = getByTestId(`device-tile-${deviceId}`); const tile = getByTestId(`device-tile-${deviceId}`);
const toggle = tile.querySelector('[aria-label="Toggle device details"]') as Element; const toggle = tile.querySelector('[aria-label="Toggle device details"]') as Element;
@ -115,11 +115,18 @@ describe('<SessionManagerTab />', () => {
const toggleDeviceSelection = ( const toggleDeviceSelection = (
getByTestId: ReturnType<typeof render>['getByTestId'], getByTestId: ReturnType<typeof render>['getByTestId'],
deviceId: ExtendedDevice['device_id'], deviceId: ExtendedDevice['device_id'],
) => { ): void => {
const checkbox = getByTestId(`device-tile-checkbox-${deviceId}`); const checkbox = getByTestId(`device-tile-checkbox-${deviceId}`);
fireEvent.click(checkbox); fireEvent.click(checkbox);
}; };
const getDeviceTile = (
getByTestId: ReturnType<typeof render>['getByTestId'],
deviceId: ExtendedDevice['device_id'],
): HTMLElement => {
return getByTestId(`device-tile-${deviceId}`);
};
const setFilter = async ( const setFilter = async (
container: HTMLElement, container: HTMLElement,
option: DeviceSecurityVariation | string, option: DeviceSecurityVariation | string,
@ -749,6 +756,7 @@ describe('<SessionManagerTab />', () => {
it('deletes multiple devices', async () => { it('deletes multiple devices', async () => {
mockClient.getDevices.mockResolvedValue({ devices: [ mockClient.getDevices.mockResolvedValue({ devices: [
alicesDevice, alicesMobileDevice, alicesOlderMobileDevice, alicesDevice, alicesMobileDevice, alicesOlderMobileDevice,
alicesInactiveDevice,
] }); ] });
mockClient.deleteMultipleDevices.mockResolvedValue({}); mockClient.deleteMultipleDevices.mockResolvedValue({});
@ -763,6 +771,24 @@ describe('<SessionManagerTab />', () => {
fireEvent.click(getByTestId('sign-out-selection-cta')); 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 // delete called with both ids
expect(mockClient.deleteMultipleDevices).toHaveBeenCalledWith( expect(mockClient.deleteMultipleDevices).toHaveBeenCalledWith(
[ [