1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-01-02 12:47:41 +02:00

Chore: Mobile: Plugin settings screen: Improve accessibility and tests (#10267)

This commit is contained in:
Henry Heino 2024-04-08 04:36:40 -07:00 committed by GitHub
parent 03c3feef16
commit a2071bfed2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 73 additions and 37 deletions

View File

@ -577,7 +577,8 @@ packages/app-mobile/components/screens/ConfigScreen/SettingItem.js
packages/app-mobile/components/screens/ConfigScreen/SettingsButton.js
packages/app-mobile/components/screens/ConfigScreen/SettingsToggle.js
packages/app-mobile/components/screens/ConfigScreen/configScreenStyles.js
packages/app-mobile/components/screens/ConfigScreen/plugins/PluginBox.js
packages/app-mobile/components/screens/ConfigScreen/plugins/PluginBox/ActionButton.js
packages/app-mobile/components/screens/ConfigScreen/plugins/PluginBox/index.js
packages/app-mobile/components/screens/ConfigScreen/plugins/PluginStates.test.js
packages/app-mobile/components/screens/ConfigScreen/plugins/PluginStates.js
packages/app-mobile/components/screens/ConfigScreen/plugins/PluginToggle.js

3
.gitignore vendored
View File

@ -557,7 +557,8 @@ packages/app-mobile/components/screens/ConfigScreen/SettingItem.js
packages/app-mobile/components/screens/ConfigScreen/SettingsButton.js
packages/app-mobile/components/screens/ConfigScreen/SettingsToggle.js
packages/app-mobile/components/screens/ConfigScreen/configScreenStyles.js
packages/app-mobile/components/screens/ConfigScreen/plugins/PluginBox.js
packages/app-mobile/components/screens/ConfigScreen/plugins/PluginBox/ActionButton.js
packages/app-mobile/components/screens/ConfigScreen/plugins/PluginBox/index.js
packages/app-mobile/components/screens/ConfigScreen/plugins/PluginStates.test.js
packages/app-mobile/components/screens/ConfigScreen/plugins/PluginStates.js
packages/app-mobile/components/screens/ConfigScreen/plugins/PluginToggle.js

View File

@ -0,0 +1,35 @@
import * as React from 'react';
import { useCallback } from 'react';
import { ItemEvent, PluginItem } from '@joplin/lib/components/shared/config/plugins/types';
import { Button, ButtonProps } from 'react-native-paper';
export type PluginCallback = (event: ItemEvent)=> void;
interface Props extends Omit<ButtonProps, 'item'|'onPress'|'children'> {
item: PluginItem;
onPress?: PluginCallback;
title: string;
}
const ActionButton: React.FC<Props> = props => {
const onPress = useCallback(() => {
props.onPress?.({ item: props.item });
}, [props.onPress, props.item]);
// Include additional information about the button when using a screen
// reader (helps make it clear which plugin a delete/enable button).
//
// Because this is being read by a screen reader and to reduce load on
// translators, the method of joining the title and manifest name is not
// marked as translatable.
const accessibilityLabel = `${props.title} ${props.item.manifest.name}`;
return (
<Button
{...props}
onPress={onPress}
accessibilityLabel={accessibilityLabel}
>{props.title}</Button>
);
};
export default ActionButton;

View File

@ -1,10 +1,11 @@
import * as React from 'react';
import { Icon, Button, Card, Chip } from 'react-native-paper';
import { Icon, Card, Chip } from 'react-native-paper';
import { _ } from '@joplin/lib/locale';
import { View } from 'react-native';
import { ItemEvent, PluginItem } from '@joplin/lib/components/shared/config/plugins/types';
import { PluginItem } from '@joplin/lib/components/shared/config/plugins/types';
import shim from '@joplin/lib/shim';
import PluginService from '@joplin/lib/services/plugins/PluginService';
import ActionButton, { PluginCallback } from './ActionButton';
export enum InstallState {
NotInstalled,
@ -19,8 +20,6 @@ export enum UpdateState {
HasBeenUpdated = 4,
}
type PluginCallback = (event: ItemEvent)=> void;
interface Props {
item: PluginItem;
isCompatible: boolean;
@ -52,41 +51,42 @@ const PluginBox: React.FC<Props> = props => {
};
const installButton = (
<Button
onPress={() => props.onInstall?.({ item })}
<ActionButton
item={item}
onPress={props.onInstall}
disabled={props.installState !== InstallState.NotInstalled || !props.isCompatible}
loading={props.installState === InstallState.Installing}
>
{installButtonTitle()}
</Button>
title={installButtonTitle()}
/>
);
const updateButtonTitle = () => {
const getUpdateButtonTitle = () => {
if (props.updateState === UpdateState.Updating) return _('Updating...');
if (props.updateState === UpdateState.HasBeenUpdated) return _('Updated');
return _('Update');
};
const updateButton = (
<Button
onPress={() => props.onUpdate?.({ item })}
<ActionButton
item={item}
onPress={props.onUpdate}
disabled={props.updateState !== UpdateState.CanUpdate || !props.isCompatible}
loading={props.updateState === UpdateState.Updating}
>
{updateButtonTitle()}
</Button>
title={getUpdateButtonTitle()}
/>
);
const deleteButton = (
<Button
onPress={() => props.onDelete?.({ item })}
<ActionButton
item={item}
onPress={props.onDelete}
disabled={props.item.deleted}
>
{props.item.deleted ? _('Deleted') : _('Delete')}
</Button>
title={props.item.deleted ? _('Deleted') : _('Delete')}
/>
);
const disableButton = <Button onPress={() => props.onToggle?.({ item })}>{_('Disable')}</Button>;
const enableButton = <Button onPress={() => props.onToggle?.({ item })}>{_('Enable')}</Button>;
const aboutButton = <Button icon='web' onPress={() => props.onAboutPress?.({ item })}>{_('About')}</Button>;
const disableButton = <ActionButton item={item} onPress={props.onToggle} title={_('Disable')}/>;
const enableButton = <ActionButton item={item} onPress={props.onToggle} title={_('Enable')}/>;
const aboutButton = <ActionButton icon='web' item={item} onPress={props.onAboutPress} title={_('About')}/>;
const renderErrorsChip = () => {
if (!props.hasErrors) return null;
@ -97,7 +97,7 @@ const PluginBox: React.FC<Props> = props => {
mode='outlined'
onPress={() => props.onShowPluginLog({ item })}
>
Error
{_('Error')}
</Chip>
);
};

View File

@ -2,7 +2,7 @@ import * as React from 'react';
import RepositoryApi from '@joplin/lib/services/plugins/RepositoryApi';
import { afterAllCleanUp, afterEachCleanUp, createTempDir, mockMobilePlatform, setupDatabaseAndSynchronizer, supportDir, switchClient } from '@joplin/lib/testing/test-utils';
import { render, screen, waitFor } from '@testing-library/react-native';
import { render, screen } from '@testing-library/react-native';
import '@testing-library/react-native/extend-expect';
import Setting from '@joplin/lib/models/Setting';
@ -124,18 +124,17 @@ describe('PluginStates', () => {
initialPluginSettings={defaultPluginSettings}
/>,
);
expect(await screen.findByText('ABC Sheet Music')).not.toBeNull();
expect(await screen.findByText('Backlinks to note')).not.toBeNull();
expect(await screen.findByText('ABC Sheet Music')).toBeVisible();
expect(await screen.findByText('Backlinks to note')).toBeVisible();
const shouldBothBeUpdatable = platform === 'android';
await waitFor(async () => {
const updateButtons = await screen.findAllByText('Update');
expect(updateButtons).toHaveLength(shouldBothBeUpdatable ? 2 : 1);
});
expect(await screen.findByRole('button', { name: 'Update ABC Sheet Music', disabled: false })).toBeVisible();
const updateButtons = await screen.findAllByText('Update');
for (const button of updateButtons) {
expect(button).not.toBeDisabled();
// Backlinks to note should not be updatable on iOS (it's not _recommended).
const backlinksToNoteQuery = { name: 'Update Backlinks to note', disabled: false };
if (platform === 'android') {
expect(await screen.findByRole('button', backlinksToNoteQuery)).toBeVisible();
} else {
expect(await screen.queryByRole('button', backlinksToNoteQuery)).toBeNull();
}
});
});