Commit b1ddafbf authored by Lyza Danger Gardner's avatar Lyza Danger Gardner Committed by Lyza Gardner

Extract `ExportAnnotations` component

- Add basic tests
parent c5e8702b
import { Button, CardActions, Input } from '@hypothesis/frontend-shared';
import { useSidebarStore } from '../../store';
import LoadingSpinner from './LoadingSpinner';
/**
* Render content for "export" tab panel
*/
export default function ExportAnnotations() {
const store = useSidebarStore();
const group = store.focusedGroup();
const exportReady = group && !store.isLoading();
const annotations = store.allAnnotations();
if (!exportReady) {
return <LoadingSpinner />;
}
// TODO: Handle 0 annotations
return (
<>
<p>
Export <strong>{annotations.length} annotations</strong> in a file
named:
</p>
<Input id="export-filename" value="filename-tbd-export.json" />
<CardActions>
<Button variant="primary" disabled>
Export
</Button>
</CardActions>
</>
);
}
import { import {
Button,
Card, Card,
CardActions,
CopyIcon, CopyIcon,
IconButton, IconButton,
Input, Input,
...@@ -18,6 +16,7 @@ import { useSidebarStore } from '../../store'; ...@@ -18,6 +16,7 @@ import { useSidebarStore } from '../../store';
import { copyText } from '../../util/copy-to-clipboard'; import { copyText } from '../../util/copy-to-clipboard';
import ShareLinks from '../ShareLinks'; import ShareLinks from '../ShareLinks';
import SidebarPanel from '../SidebarPanel'; import SidebarPanel from '../SidebarPanel';
import ExportAnnotations from './ExportAnnotations';
import LoadingSpinner from './LoadingSpinner'; import LoadingSpinner from './LoadingSpinner';
import TabHeader from './TabHeader'; import TabHeader from './TabHeader';
import TabPanel from './TabPanel'; import TabPanel from './TabPanel';
...@@ -110,38 +109,6 @@ function SharePanelContent({ ...@@ -110,38 +109,6 @@ function SharePanelContent({
); );
} }
type ExportPanelContentProps = {
loading: boolean;
annotationCount: number;
};
/**
* Render content for "export" tab panel
*/
function ExportPanelContent({
loading,
annotationCount,
}: ExportPanelContentProps) {
if (loading) {
return <LoadingSpinner />;
}
// TODO: Handle 0 annotations
return (
<>
<p>
Export <strong>{annotationCount} annotations</strong> in a file named:
</p>
<Input id="export-filename" value="filename-tbd-export.json" />
<CardActions>
<Button variant="primary" disabled>
Export
</Button>
</CardActions>
</>
);
}
export type ShareDialogProps = { export type ShareDialogProps = {
// injected // injected
toastMessenger: ToastMessengerService; toastMessenger: ToastMessengerService;
...@@ -160,7 +127,6 @@ function ShareDialog({ toastMessenger }: ShareDialogProps) { ...@@ -160,7 +127,6 @@ function ShareDialog({ toastMessenger }: ShareDialogProps) {
const focusedGroup = store.focusedGroup(); const focusedGroup = store.focusedGroup();
const groupName = (focusedGroup && focusedGroup.name) || '...'; const groupName = (focusedGroup && focusedGroup.name) || '...';
const panelTitle = `Share Annotations in ${groupName}`; const panelTitle = `Share Annotations in ${groupName}`;
const allAnnotations = store.allAnnotations();
const tabbedDialog = store.isFeatureEnabled('export_annotations'); const tabbedDialog = store.isFeatureEnabled('export_annotations');
const [selectedTab, setSelectedTab] = useState<'share' | 'export'>('share'); const [selectedTab, setSelectedTab] = useState<'share' | 'export'>('share');
...@@ -169,7 +135,6 @@ function ShareDialog({ toastMessenger }: ShareDialogProps) { ...@@ -169,7 +135,6 @@ function ShareDialog({ toastMessenger }: ShareDialogProps) {
// be available // be available
const sharingReady = focusedGroup && mainFrame; const sharingReady = focusedGroup && mainFrame;
// Show a loading spinner in the export tab if annotations are loading // Show a loading spinner in the export tab if annotations are loading
const exportReady = focusedGroup && !store.isLoading();
const shareURI = const shareURI =
sharingReady && pageSharingLink(mainFrame.uri, focusedGroup.id); sharingReady && pageSharingLink(mainFrame.uri, focusedGroup.id);
...@@ -237,10 +202,7 @@ function ShareDialog({ toastMessenger }: ShareDialogProps) { ...@@ -237,10 +202,7 @@ function ShareDialog({ toastMessenger }: ShareDialogProps) {
aria-labelledby="export-panel-tab" aria-labelledby="export-panel-tab"
title={`Export from ${focusedGroup?.name ?? '...'}`} title={`Export from ${focusedGroup?.name ?? '...'}`}
> >
<ExportPanelContent <ExportAnnotations />
annotationCount={allAnnotations.length}
loading={!exportReady}
/>
</TabPanel> </TabPanel>
</Card> </Card>
</> </>
......
import { mount } from 'enzyme';
import { checkAccessibility } from '../../../../test-util/accessibility';
import { mockImportedComponents } from '../../../../test-util/mock-imported-components';
import ExportAnnotations from '../ExportAnnotations';
import { $imports } from '../ExportAnnotations';
describe('ExportAnnotations', () => {
let fakeStore;
const fakePrivateGroup = {
type: 'private',
name: 'Test Private Group',
id: 'testprivate',
};
const createComponent = props => mount(<ExportAnnotations {...props} />);
beforeEach(() => {
fakeStore = {
allAnnotations: sinon.stub().returns(0),
focusedGroup: sinon.stub().returns(fakePrivateGroup),
isLoading: sinon.stub().returns(false),
};
$imports.$mock(mockImportedComponents());
$imports.$mock({
'../../store': { useSidebarStore: () => fakeStore },
});
});
afterEach(() => {
$imports.$restore();
});
context('export annotations not ready', () => {
it('renders a loading spinner if there is no focused group', () => {
fakeStore.focusedGroup.returns(null);
const wrapper = createComponent();
assert.isTrue(wrapper.find('LoadingSpinner').exists());
});
it('renders a loading spinner if annotations are loading', () => {
fakeStore.isLoading.returns(true);
const wrapper = createComponent();
assert.isTrue(wrapper.find('LoadingSpinner').exists());
});
});
it('provides a filename field', () => {
// TODO expand as component logic is implemented
const wrapper = createComponent();
assert.isTrue(wrapper.find('Input').exists());
});
it(
'should pass a11y checks',
checkAccessibility({
content: () => createComponent(),
})
);
});
...@@ -45,7 +45,7 @@ describe('ShareDialog', () => { ...@@ -45,7 +45,7 @@ describe('ShareDialog', () => {
}; };
$imports.$mock(mockImportedComponents()); $imports.$mock(mockImportedComponents());
// Don't mock these simple components for now // Don't mock these related components for now
$imports.$restore({ $imports.$restore({
'./LoadingSpinner': true, './LoadingSpinner': true,
'./TabHeader': true, './TabHeader': true,
...@@ -246,7 +246,6 @@ describe('ShareDialog', () => { ...@@ -246,7 +246,6 @@ describe('ShareDialog', () => {
.find('TabPanel') .find('TabPanel')
.filter({ active: true }); .filter({ active: true });
assert.equal(activeTabPanel.props().id, 'export-panel'); assert.equal(activeTabPanel.props().id, 'export-panel');
assert.isTrue(activeTabPanel.find('Input').exists());
// Now, reselect share tab // Now, reselect share tab
act(() => { act(() => {
...@@ -260,29 +259,6 @@ describe('ShareDialog', () => { ...@@ -260,29 +259,6 @@ describe('ShareDialog', () => {
const shareTabPanel = wrapper.find('TabPanel').filter({ active: true }); const shareTabPanel = wrapper.find('TabPanel').filter({ active: true });
assert.equal(shareTabPanel.props().id, 'share-panel'); assert.equal(shareTabPanel.props().id, 'share-panel');
}); });
it('shows a loading indicator on the export tab if not ready', () => {
const wrapper = createComponent();
const exportTabSelector = 'Tab[aria-controls="export-panel"]';
fakeStore.isLoading.returns(true);
act(() => {
wrapper
.find(exportTabSelector)
.getDOMNode()
.dispatchEvent(new Event('click'));
});
wrapper.update();
const activeTabPanel = wrapper
.find('TabPanel')
.filter({ active: true });
assert.equal(activeTabPanel.props().id, 'export-panel');
assert.isFalse(activeTabPanel.find('Input').exists());
assert.isTrue(
activeTabPanel.find('[data-testid="loading-spinner"]').exists()
);
});
}); });
}); });
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment