@jbrowse/plugin-menus 2.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/LICENSE +201 -0
  2. package/dist/AboutWidget/components/AboutWidget.d.ts +7 -0
  3. package/dist/AboutWidget/components/AboutWidget.js +63 -0
  4. package/dist/AboutWidget/components/AboutWidget.js.map +1 -0
  5. package/dist/AboutWidget/index.d.ts +5 -0
  6. package/dist/AboutWidget/index.js +12 -0
  7. package/dist/AboutWidget/index.js.map +1 -0
  8. package/dist/HelpWidget/components/HelpWidget.d.ts +7 -0
  9. package/dist/HelpWidget/components/HelpWidget.js +37 -0
  10. package/dist/HelpWidget/components/HelpWidget.js.map +1 -0
  11. package/dist/HelpWidget/index.d.ts +5 -0
  12. package/dist/HelpWidget/index.js +12 -0
  13. package/dist/HelpWidget/index.js.map +1 -0
  14. package/dist/ImportSessionWidget/components/ImportError.d.ts +4 -0
  15. package/dist/ImportSessionWidget/components/ImportError.js +35 -0
  16. package/dist/ImportSessionWidget/components/ImportError.js.map +1 -0
  17. package/dist/ImportSessionWidget/components/ImportSessionWidget.d.ts +7 -0
  18. package/dist/ImportSessionWidget/components/ImportSessionWidget.js +114 -0
  19. package/dist/ImportSessionWidget/components/ImportSessionWidget.js.map +1 -0
  20. package/dist/ImportSessionWidget/index.d.ts +5 -0
  21. package/dist/ImportSessionWidget/index.js +12 -0
  22. package/dist/ImportSessionWidget/index.js.map +1 -0
  23. package/dist/SessionManager/components/DeleteDialog.d.ts +6 -0
  24. package/dist/SessionManager/components/DeleteDialog.js +18 -0
  25. package/dist/SessionManager/components/DeleteDialog.js.map +1 -0
  26. package/dist/SessionManager/components/SessionManager.d.ts +20 -0
  27. package/dist/SessionManager/components/SessionManager.js +102 -0
  28. package/dist/SessionManager/components/SessionManager.js.map +1 -0
  29. package/dist/SessionManager/index.d.ts +5 -0
  30. package/dist/SessionManager/index.js +12 -0
  31. package/dist/SessionManager/index.js.map +1 -0
  32. package/dist/index.d.ts +7 -0
  33. package/dist/index.js +104 -0
  34. package/dist/index.js.map +1 -0
  35. package/esm/AboutWidget/components/AboutWidget.d.ts +7 -0
  36. package/esm/AboutWidget/components/AboutWidget.js +58 -0
  37. package/esm/AboutWidget/components/AboutWidget.js.map +1 -0
  38. package/esm/AboutWidget/index.d.ts +5 -0
  39. package/esm/AboutWidget/index.js +9 -0
  40. package/esm/AboutWidget/index.js.map +1 -0
  41. package/esm/HelpWidget/components/HelpWidget.d.ts +7 -0
  42. package/esm/HelpWidget/components/HelpWidget.js +32 -0
  43. package/esm/HelpWidget/components/HelpWidget.js.map +1 -0
  44. package/esm/HelpWidget/index.d.ts +5 -0
  45. package/esm/HelpWidget/index.js +9 -0
  46. package/esm/HelpWidget/index.js.map +1 -0
  47. package/esm/ImportSessionWidget/components/ImportError.d.ts +4 -0
  48. package/esm/ImportSessionWidget/components/ImportError.js +29 -0
  49. package/esm/ImportSessionWidget/components/ImportError.js.map +1 -0
  50. package/esm/ImportSessionWidget/components/ImportSessionWidget.d.ts +7 -0
  51. package/esm/ImportSessionWidget/components/ImportSessionWidget.js +86 -0
  52. package/esm/ImportSessionWidget/components/ImportSessionWidget.js.map +1 -0
  53. package/esm/ImportSessionWidget/index.d.ts +5 -0
  54. package/esm/ImportSessionWidget/index.js +9 -0
  55. package/esm/ImportSessionWidget/index.js.map +1 -0
  56. package/esm/SessionManager/components/DeleteDialog.d.ts +6 -0
  57. package/esm/SessionManager/components/DeleteDialog.js +12 -0
  58. package/esm/SessionManager/components/DeleteDialog.js.map +1 -0
  59. package/esm/SessionManager/components/SessionManager.d.ts +20 -0
  60. package/esm/SessionManager/components/SessionManager.js +74 -0
  61. package/esm/SessionManager/components/SessionManager.js.map +1 -0
  62. package/esm/SessionManager/index.d.ts +5 -0
  63. package/esm/SessionManager/index.js +9 -0
  64. package/esm/SessionManager/index.js.map +1 -0
  65. package/esm/index.d.ts +7 -0
  66. package/esm/index.js +75 -0
  67. package/esm/index.js.map +1 -0
  68. package/package.json +61 -0
  69. package/src/AboutWidget/components/AboutWidget.test.tsx +35 -0
  70. package/src/AboutWidget/components/AboutWidget.tsx +91 -0
  71. package/src/AboutWidget/components/__snapshots__/AboutWidget.test.tsx.snap +65 -0
  72. package/src/AboutWidget/index.ts +10 -0
  73. package/src/HelpWidget/components/HelpWidget.test.js +10 -0
  74. package/src/HelpWidget/components/HelpWidget.tsx +75 -0
  75. package/src/HelpWidget/components/__snapshots__/HelpWidget.test.js.snap +63 -0
  76. package/src/HelpWidget/index.ts +10 -0
  77. package/src/ImportSessionWidget/components/ImportError.tsx +38 -0
  78. package/src/ImportSessionWidget/components/ImportSessionWidget.tsx +109 -0
  79. package/src/ImportSessionWidget/index.ts +10 -0
  80. package/src/SessionManager/components/DeleteDialog.tsx +41 -0
  81. package/src/SessionManager/components/SessionManager.test.tsx +14 -0
  82. package/src/SessionManager/components/SessionManager.tsx +166 -0
  83. package/src/SessionManager/components/__snapshots__/SessionManager.test.tsx.snap +22 -0
  84. package/src/SessionManager/index.ts +10 -0
  85. package/src/index.test.ts +12 -0
  86. package/src/index.ts +100 -0
@@ -0,0 +1,12 @@
1
+ import React from 'react';
2
+ import { Button, DialogActions, DialogContent, DialogContentText, } from '@mui/material';
3
+ import { Dialog } from '@jbrowse/core/ui';
4
+ export default function DeleteDialog({ open, sessionNameToDelete, handleClose, }) {
5
+ return (React.createElement(Dialog, { open: open, "aria-labelledby": "alert-dialog-title", "aria-describedby": "alert-dialog-description", title: `Delete session "${sessionNameToDelete}"?` },
6
+ React.createElement(DialogContent, null,
7
+ React.createElement(DialogContentText, { id: "alert-dialog-description" }, "This action cannot be undone")),
8
+ React.createElement(DialogActions, null,
9
+ React.createElement(Button, { onClick: () => handleClose(), color: "primary" }, "Cancel"),
10
+ React.createElement(Button, { onClick: () => handleClose(true), color: "primary", autoFocus: true }, "Delete"))));
11
+ }
12
+ //# sourceMappingURL=DeleteDialog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DeleteDialog.js","sourceRoot":"","sources":["../../../src/SessionManager/components/DeleteDialog.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EACL,MAAM,EACN,aAAa,EACb,aAAa,EACb,iBAAiB,GAClB,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAEzC,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,EACnC,IAAI,EACJ,mBAAmB,EACnB,WAAW,GAKZ;IACC,OAAO,CACL,oBAAC,MAAM,IACL,IAAI,EAAE,IAAI,qBACM,oBAAoB,sBACnB,0BAA0B,EAC3C,KAAK,EAAE,mBAAmB,mBAAmB,IAAI;QAEjD,oBAAC,aAAa;YACZ,oBAAC,iBAAiB,IAAC,EAAE,EAAC,0BAA0B,mCAE5B,CACN;QAChB,oBAAC,aAAa;YACZ,oBAAC,MAAM,IAAC,OAAO,EAAE,GAAG,EAAE,CAAC,WAAW,EAAE,EAAE,KAAK,EAAC,SAAS,aAE5C;YACT,oBAAC,MAAM,IAAC,OAAO,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,KAAK,EAAC,SAAS,EAAC,SAAS,mBAE1D,CACK,CACT,CACV,CAAA;AACH,CAAC"}
@@ -0,0 +1,20 @@
1
+ import React from 'react';
2
+ import { AbstractSessionModel } from '@jbrowse/core/util';
3
+ interface SessionSnap {
4
+ name: string;
5
+ views?: {
6
+ tracks: unknown[];
7
+ }[];
8
+ [key: string]: unknown;
9
+ }
10
+ interface SessionModel extends AbstractSessionModel {
11
+ savedSessions: SessionSnap[];
12
+ removeSavedSession: (arg: SessionSnap) => void;
13
+ activateSession: (arg: string) => void;
14
+ loadAutosaveSession: () => void;
15
+ previousAutosaveId: string;
16
+ }
17
+ declare const SessionManager: ({ session }: {
18
+ session: SessionModel;
19
+ }) => React.JSX.Element;
20
+ export default SessionManager;
@@ -0,0 +1,74 @@
1
+ import React, { useState } from 'react';
2
+ import { IconButton, List, ListItem, ListItemIcon, ListItemSecondaryAction, ListItemText, ListSubheader, Paper, Typography, } from '@mui/material';
3
+ import { makeStyles } from 'tss-react/mui';
4
+ import { observer } from 'mobx-react';
5
+ import pluralize from 'pluralize';
6
+ // icons
7
+ import DeleteIcon from '@mui/icons-material/Delete';
8
+ import ViewListIcon from '@mui/icons-material/ViewList';
9
+ import DeleteDialog from './DeleteDialog';
10
+ const useStyles = makeStyles()(theme => ({
11
+ root: {
12
+ margin: theme.spacing(1),
13
+ },
14
+ message: {
15
+ padding: theme.spacing(3),
16
+ },
17
+ }));
18
+ const AutosaveEntry = observer(({ session }) => {
19
+ const { classes } = useStyles();
20
+ const autosavedSession = JSON.parse(localStorage.getItem(session.previousAutosaveId) || '{}').session;
21
+ const { views = [] } = autosavedSession || {};
22
+ const totalTracks = views
23
+ .map(view => view.tracks.length)
24
+ .reduce((a, b) => a + b, 0);
25
+ return autosavedSession ? (React.createElement(Paper, { className: classes.root },
26
+ React.createElement(List, { subheader: React.createElement(ListSubheader, null, "Previous autosaved entry") },
27
+ React.createElement(ListItem, { button: true, onClick: () => session.loadAutosaveSession() },
28
+ React.createElement(ListItemIcon, null,
29
+ React.createElement(ViewListIcon, null)),
30
+ React.createElement(ListItemText, { primary: autosavedSession.name, secondary: session.name === autosavedSession.name
31
+ ? 'Currently open'
32
+ : `${views.length} ${pluralize('view', views.length)}; ${totalTracks}
33
+ open ${pluralize('track', totalTracks)}` }))))) : null;
34
+ });
35
+ const SessionManager = observer(({ session }) => {
36
+ const { classes } = useStyles();
37
+ const [sessionIndexToDelete, setSessionIndexToDelete] = useState();
38
+ const [open, setOpen] = useState(false);
39
+ function handleDialogClose(deleteSession = false) {
40
+ if (deleteSession && sessionIndexToDelete !== undefined) {
41
+ session.removeSavedSession(session.savedSessions[sessionIndexToDelete]);
42
+ }
43
+ setSessionIndexToDelete(undefined);
44
+ setOpen(false);
45
+ }
46
+ const sessionNameToDelete = sessionIndexToDelete !== undefined
47
+ ? session.savedSessions[sessionIndexToDelete].name
48
+ : '';
49
+ return (React.createElement(React.Fragment, null,
50
+ React.createElement(AutosaveEntry, { session: session }),
51
+ React.createElement(Paper, { className: classes.root },
52
+ React.createElement(List, { subheader: React.createElement(ListSubheader, null, "Saved sessions") }, session.savedSessions.length ? (session.savedSessions.map((sessionSnapshot, idx) => {
53
+ const { views = [] } = sessionSnapshot;
54
+ const totalTracks = views
55
+ .map(view => view.tracks.length)
56
+ .reduce((a, b) => a + b, 0);
57
+ return (React.createElement(ListItem, { button: true, disabled: session.name === sessionSnapshot.name, onClick: () => session.activateSession(sessionSnapshot.name), key: sessionSnapshot.name },
58
+ React.createElement(ListItemIcon, null,
59
+ React.createElement(ViewListIcon, null)),
60
+ React.createElement(ListItemText, { primary: sessionSnapshot.name, secondary: session.name === sessionSnapshot.name
61
+ ? 'Currently open'
62
+ : `${views.length} ${pluralize('view', views.length)}; ${totalTracks}
63
+ open ${pluralize('track', totalTracks)}` }),
64
+ React.createElement(ListItemSecondaryAction, null,
65
+ React.createElement(IconButton, { edge: "end", disabled: session.name === sessionSnapshot.name, "aria-label": "Delete", onClick: () => {
66
+ setSessionIndexToDelete(idx);
67
+ setOpen(true);
68
+ } },
69
+ React.createElement(DeleteIcon, null)))));
70
+ })) : (React.createElement(Typography, { className: classes.message }, "No saved sessions found")))),
71
+ React.createElement(DeleteDialog, { open: open, sessionNameToDelete: sessionNameToDelete, handleClose: handleDialogClose })));
72
+ });
73
+ export default SessionManager;
74
+ //# sourceMappingURL=SessionManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SessionManager.js","sourceRoot":"","sources":["../../../src/SessionManager/components/SessionManager.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export declare const configSchema: import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{}, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined>>;
2
+ export declare const stateModel: import("mobx-state-tree").IModelType<{
3
+ id: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<string>, [undefined]>;
4
+ type: import("mobx-state-tree").ISimpleType<"SessionManager">;
5
+ }, {}, import("mobx-state-tree")._NotCustomized, import("mobx-state-tree")._NotCustomized>;
@@ -0,0 +1,9 @@
1
+ import { ConfigurationSchema } from '@jbrowse/core/configuration';
2
+ import { ElementId } from '@jbrowse/core/util/types/mst';
3
+ import { types } from 'mobx-state-tree';
4
+ export const configSchema = ConfigurationSchema('SessionManager', {});
5
+ export const stateModel = types.model('SessionManager', {
6
+ id: ElementId,
7
+ type: types.literal('SessionManager'),
8
+ });
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/SessionManager/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AACjE,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAA;AACxD,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAA;AAEvC,MAAM,CAAC,MAAM,YAAY,GAAG,mBAAmB,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAA;AAErE,MAAM,CAAC,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,gBAAgB,EAAE;IACtD,EAAE,EAAE,SAAS;IACb,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,gBAAgB,CAAC;CACtC,CAAC,CAAA"}
package/esm/index.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ import Plugin from '@jbrowse/core/Plugin';
2
+ import PluginManager from '@jbrowse/core/PluginManager';
3
+ export default class extends Plugin {
4
+ name: string;
5
+ install(pluginManager: PluginManager): void;
6
+ configure(pluginManager: PluginManager): void;
7
+ }
package/esm/index.js ADDED
@@ -0,0 +1,75 @@
1
+ import { lazy } from 'react';
2
+ import WidgetType from '@jbrowse/core/pluggableElementTypes/WidgetType';
3
+ import Plugin from '@jbrowse/core/Plugin';
4
+ import { isAbstractMenuManager } from '@jbrowse/core/util';
5
+ import HelpIcon from '@mui/icons-material/Help';
6
+ import InfoIcon from '@mui/icons-material/Info';
7
+ import { configSchema as aboutConfigSchema, stateModel as aboutStateModel, } from './AboutWidget';
8
+ import { configSchema as helpConfigSchema, stateModel as helpStateModel, } from './HelpWidget';
9
+ import { configSchema as importSessionConfigSchema, stateModel as importSessionStateModel, } from './ImportSessionWidget';
10
+ import { configSchema as sessionManagerConfigSchema, stateModel as sessionManagerStateModel, } from './SessionManager';
11
+ export default class extends Plugin {
12
+ constructor() {
13
+ super(...arguments);
14
+ this.name = 'MenusPlugin';
15
+ }
16
+ install(pluginManager) {
17
+ pluginManager.addWidgetType(() => {
18
+ return new WidgetType({
19
+ name: 'AboutWidget',
20
+ heading: 'About',
21
+ configSchema: aboutConfigSchema,
22
+ stateModel: aboutStateModel,
23
+ ReactComponent: lazy(() => import('./AboutWidget/components/AboutWidget')),
24
+ });
25
+ });
26
+ pluginManager.addWidgetType(() => {
27
+ return new WidgetType({
28
+ name: 'HelpWidget',
29
+ heading: 'Help',
30
+ configSchema: helpConfigSchema,
31
+ stateModel: helpStateModel,
32
+ ReactComponent: lazy(() => import('./HelpWidget/components/HelpWidget')),
33
+ });
34
+ });
35
+ pluginManager.addWidgetType(() => {
36
+ return new WidgetType({
37
+ name: 'ImportSessionWidget',
38
+ heading: 'Import session',
39
+ configSchema: importSessionConfigSchema,
40
+ stateModel: importSessionStateModel,
41
+ ReactComponent: lazy(() => import('./ImportSessionWidget/components/ImportSessionWidget')),
42
+ });
43
+ });
44
+ pluginManager.addWidgetType(() => {
45
+ return new WidgetType({
46
+ name: 'SessionManager',
47
+ heading: 'Sessions',
48
+ configSchema: sessionManagerConfigSchema,
49
+ stateModel: sessionManagerStateModel,
50
+ ReactComponent: lazy(() => import('./SessionManager/components/SessionManager')),
51
+ });
52
+ });
53
+ }
54
+ configure(pluginManager) {
55
+ if (isAbstractMenuManager(pluginManager.rootModel)) {
56
+ pluginManager.rootModel.appendToMenu('Help', {
57
+ label: 'About',
58
+ icon: InfoIcon,
59
+ onClick: (session) => {
60
+ const widget = session.addWidget('AboutWidget', 'aboutWidget');
61
+ session.showWidget(widget);
62
+ },
63
+ });
64
+ pluginManager.rootModel.appendToMenu('Help', {
65
+ label: 'Help',
66
+ icon: HelpIcon,
67
+ onClick: (session) => {
68
+ const widget = session.addWidget('HelpWidget', 'helpWidget');
69
+ session.showWidget(widget);
70
+ },
71
+ });
72
+ }
73
+ }
74
+ }
75
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@jbrowse/plugin-menus",
3
+ "version": "2.6.1",
4
+ "description": "JBrowse 2 basic menus",
5
+ "keywords": [
6
+ "jbrowse",
7
+ "jbrowse2"
8
+ ],
9
+ "license": "Apache-2.0",
10
+ "homepage": "https://jbrowse.org",
11
+ "bugs": "https://github.com/GMOD/jbrowse-components/issues",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/GMOD/jbrowse-components.git",
15
+ "directory": "plugins/menus"
16
+ },
17
+ "author": "JBrowse Team",
18
+ "distMain": "dist/index.js",
19
+ "srcMain": "src/index.ts",
20
+ "main": "dist/index.js",
21
+ "files": [
22
+ "dist",
23
+ "src",
24
+ "esm"
25
+ ],
26
+ "scripts": {
27
+ "build": "npm-run-all build:*",
28
+ "test": "cd ../..; jest plugins/menus",
29
+ "prepublishOnly": "yarn test",
30
+ "prepack": "yarn build && yarn useDist",
31
+ "postpack": "yarn useSrc",
32
+ "useDist": "node ../../scripts/useDist.js",
33
+ "useSrc": "node ../../scripts/useSrc.js",
34
+ "prebuild": "npm run clean",
35
+ "build:esm": "tsc --build tsconfig.build.esm.json",
36
+ "build:es5": "tsc --build tsconfig.build.es5.json",
37
+ "clean": "rimraf dist esm *.tsbuildinfo"
38
+ },
39
+ "dependencies": {
40
+ "@mui/icons-material": "^5.0.1",
41
+ "pluralize": "^8.0.0",
42
+ "react-dropzone": "^14.2.1"
43
+ },
44
+ "peerDependencies": {
45
+ "@jbrowse/core": "^2.0.0",
46
+ "@mui/material": "^5.0.0",
47
+ "mobx": "^6.0.0",
48
+ "mobx-react": "^7.0.0",
49
+ "mobx-state-tree": "^5.0.0",
50
+ "react": ">=16.8.0",
51
+ "react-dom": ">=16.8.0",
52
+ "tss-react": "^4.0.0"
53
+ },
54
+ "distModule": "esm/index.js",
55
+ "srcModule": "src/index.ts",
56
+ "module": "esm/index.js",
57
+ "publishConfig": {
58
+ "access": "public"
59
+ },
60
+ "gitHead": "1cbe7ba097fb2d2763c776e5e429e4670cdd583c"
61
+ }
@@ -0,0 +1,35 @@
1
+ import React from 'react'
2
+ import { render } from '@testing-library/react'
3
+ import { types } from 'mobx-state-tree'
4
+ import { ConfigurationSchema } from '@jbrowse/core/configuration'
5
+
6
+ // locals
7
+ import AboutWidget from './AboutWidget'
8
+
9
+ describe('<AboutWidget />', () => {
10
+ it('renders', () => {
11
+ const session = types
12
+ .model({
13
+ configuration: ConfigurationSchema('Null', {}),
14
+ rpcManager: types.frozen({}),
15
+ widgetModel: types.model({ type: types.literal('AboutWidget') }),
16
+ })
17
+ .create(
18
+ { widgetModel: { type: 'AboutWidget' } },
19
+ {
20
+ pluginManager: {
21
+ pluginMetadata: {},
22
+ plugins: [
23
+ {
24
+ name: 'HelloPlugin',
25
+ version: '1.0.0',
26
+ url: 'http://google.com',
27
+ },
28
+ ],
29
+ },
30
+ },
31
+ )
32
+ const { container } = render(<AboutWidget model={session.widgetModel} />)
33
+ expect(container.firstChild).toMatchSnapshot()
34
+ })
35
+ })
@@ -0,0 +1,91 @@
1
+ import React from 'react'
2
+ import { observer } from 'mobx-react'
3
+ import { IAnyStateTreeNode, getEnv } from 'mobx-state-tree'
4
+ import { getSession } from '@jbrowse/core/util'
5
+ import { Typography, Link } from '@mui/material'
6
+ import { makeStyles } from 'tss-react/mui'
7
+ import PluginManager from '@jbrowse/core/PluginManager'
8
+
9
+ const useStyles = makeStyles()(theme => ({
10
+ root: {
11
+ margin: theme.spacing(2),
12
+ paddingTop: theme.spacing(2),
13
+ },
14
+ subtitle: {
15
+ margin: theme.spacing(1),
16
+ },
17
+ pluginList: {
18
+ margin: theme.spacing(1),
19
+ marginTop: theme.spacing(5),
20
+ },
21
+ }))
22
+
23
+ function About({ model }: { model: IAnyStateTreeNode }) {
24
+ const { classes } = useStyles()
25
+ const { version } = getSession(model)
26
+ const { pluginManager } = getEnv(model)
27
+ const { plugins } = pluginManager as PluginManager
28
+ const corePlugins = new Set(
29
+ plugins
30
+ .filter(p => pluginManager.pluginMetadata[p.name]?.isCore)
31
+ .map(p => p.name),
32
+ )
33
+
34
+ return (
35
+ <div className={classes.root}>
36
+ <Typography variant="h4" align="center">
37
+ JBrowse 2
38
+ </Typography>
39
+ <Typography variant="h6" align="center" className={classes.subtitle}>
40
+ {version}
41
+ </Typography>
42
+ <Typography align="center">
43
+ JBrowse is a{' '}
44
+ <Link href="http://gmod.org/" target="_blank" rel="noopener noreferrer">
45
+ GMOD
46
+ </Link>{' '}
47
+ project
48
+ </Typography>
49
+ <br />
50
+ <Typography align="center">
51
+ © 2019-2022 The Evolutionary Software Foundation
52
+ </Typography>
53
+ <div className={classes.pluginList}>
54
+ <Typography>External plugins loaded</Typography>
55
+ <ul>
56
+ {plugins
57
+ .filter(plugin => !corePlugins.has(plugin.name))
58
+ .map(plugin => {
59
+ const { url, name, version = '' } = plugin
60
+ const text = `${name} ${version || ''}`
61
+ return (
62
+ <li key={plugin.name}>
63
+ {plugin.url ? (
64
+ <Link target="_blank" rel="noopener noreferrer" href={url}>
65
+ {text}
66
+ </Link>
67
+ ) : (
68
+ <Typography>{text}</Typography>
69
+ )}
70
+ </li>
71
+ )
72
+ })}
73
+ </ul>
74
+ <Typography>Core plugins loaded</Typography>
75
+ <ul>
76
+ {plugins
77
+ .filter(plugin => corePlugins.has(plugin.name))
78
+ .map(plugin => (
79
+ <li key={plugin.name}>
80
+ <Typography>
81
+ {plugin.name} {plugin.version || ''}
82
+ </Typography>
83
+ </li>
84
+ ))}
85
+ </ul>
86
+ </div>
87
+ </div>
88
+ )
89
+ }
90
+
91
+ export default observer(About)
@@ -0,0 +1,65 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`<AboutWidget /> renders 1`] = `
4
+ <div
5
+ class="css-1a8qml5-root"
6
+ >
7
+ <h4
8
+ class="MuiTypography-root MuiTypography-h4 MuiTypography-alignCenter css-149xwtl-MuiTypography-root"
9
+ >
10
+ JBrowse 2
11
+ </h4>
12
+ <h6
13
+ class="MuiTypography-root MuiTypography-h6 MuiTypography-alignCenter css-1l8stdg-MuiTypography-root-subtitle"
14
+ />
15
+ <p
16
+ class="MuiTypography-root MuiTypography-body1 MuiTypography-alignCenter css-1uwgr7b-MuiTypography-root"
17
+ >
18
+ JBrowse is a
19
+
20
+ <a
21
+ class="MuiTypography-root MuiTypography-inherit MuiLink-root MuiLink-underlineAlways css-1bhi0za-MuiTypography-root-MuiLink-root"
22
+ href="http://gmod.org/"
23
+ rel="noopener noreferrer"
24
+ target="_blank"
25
+ >
26
+ GMOD
27
+ </a>
28
+
29
+ project
30
+ </p>
31
+ <br />
32
+ <p
33
+ class="MuiTypography-root MuiTypography-body1 MuiTypography-alignCenter css-1uwgr7b-MuiTypography-root"
34
+ >
35
+ © 2019-2022 The Evolutionary Software Foundation
36
+ </p>
37
+ <div
38
+ class="css-la8yjp-pluginList"
39
+ >
40
+ <p
41
+ class="MuiTypography-root MuiTypography-body1 css-ahj2mt-MuiTypography-root"
42
+ >
43
+ External plugins loaded
44
+ </p>
45
+ <ul>
46
+ <li>
47
+ <a
48
+ class="MuiTypography-root MuiTypography-inherit MuiLink-root MuiLink-underlineAlways css-1bhi0za-MuiTypography-root-MuiLink-root"
49
+ href="http://google.com"
50
+ rel="noopener noreferrer"
51
+ target="_blank"
52
+ >
53
+ HelloPlugin 1.0.0
54
+ </a>
55
+ </li>
56
+ </ul>
57
+ <p
58
+ class="MuiTypography-root MuiTypography-body1 css-ahj2mt-MuiTypography-root"
59
+ >
60
+ Core plugins loaded
61
+ </p>
62
+ <ul />
63
+ </div>
64
+ </div>
65
+ `;
@@ -0,0 +1,10 @@
1
+ import { ConfigurationSchema } from '@jbrowse/core/configuration'
2
+ import { ElementId } from '@jbrowse/core/util/types/mst'
3
+ import { types } from 'mobx-state-tree'
4
+
5
+ export const configSchema = ConfigurationSchema('AboutWidget', {})
6
+
7
+ export const stateModel = types.model('AboutWidget', {
8
+ id: ElementId,
9
+ type: types.literal('AboutWidget'),
10
+ })
@@ -0,0 +1,10 @@
1
+ import React from 'react'
2
+ import { render } from '@testing-library/react'
3
+ import HelpWidget from './HelpWidget'
4
+
5
+ describe('<HelpWidget />', () => {
6
+ it('renders', () => {
7
+ const { container } = render(<HelpWidget />)
8
+ expect(container.firstChild).toMatchSnapshot()
9
+ })
10
+ })
@@ -0,0 +1,75 @@
1
+ import React from 'react'
2
+ import { observer } from 'mobx-react'
3
+ import { IAnyStateTreeNode } from 'mobx-state-tree'
4
+ import { getSession } from '@jbrowse/core/util'
5
+ import { makeStyles } from 'tss-react/mui'
6
+ import { Link, Typography } from '@mui/material'
7
+
8
+ const useStyles = makeStyles()(theme => ({
9
+ root: {
10
+ margin: theme.spacing(2),
11
+ },
12
+ subtitle: {
13
+ margin: theme.spacing(1),
14
+ },
15
+ }))
16
+
17
+ function Help({ model }: { model?: IAnyStateTreeNode }) {
18
+ const { classes } = useStyles()
19
+ const root = model ? getSession(model) : { version: '' }
20
+ return (
21
+ <div className={classes.root}>
22
+ <Typography variant="h4" align="center">
23
+ JBrowse 2
24
+ </Typography>
25
+ <Typography variant="h6" align="center" className={classes.subtitle}>
26
+ {root.version}
27
+ </Typography>
28
+
29
+ <Typography>
30
+ Here are some resources to get help. Please report the version number
31
+ above when asking questions. Thanks!
32
+ </Typography>
33
+ <ul>
34
+ <li>
35
+ <Link
36
+ href="https://github.com/GMOD/jbrowse-components/discussions"
37
+ target="_blank"
38
+ rel="noopener noreferrer"
39
+ >
40
+ Question & answer forum
41
+ </Link>
42
+ </li>
43
+ <li>
44
+ <Link
45
+ href="https://github.com/GMOD/jbrowse-components/issues/new/choose"
46
+ target="_blank"
47
+ rel="noopener noreferrer"
48
+ >
49
+ Report a bug
50
+ </Link>
51
+ </li>
52
+ <li>
53
+ <Link
54
+ href="https://jbrowse.org/jb2/docs/user_guide"
55
+ target="_blank"
56
+ rel="noopener noreferrer"
57
+ >
58
+ User guide
59
+ </Link>
60
+ </li>
61
+ <li>
62
+ <Link
63
+ href="https://jbrowse.org/jb2/docs/"
64
+ target="_blank"
65
+ rel="noopener noreferrer"
66
+ >
67
+ Documentation
68
+ </Link>
69
+ </li>
70
+ </ul>
71
+ </div>
72
+ )
73
+ }
74
+
75
+ export default observer(Help)
@@ -0,0 +1,63 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`<HelpWidget /> renders 1`] = `
4
+ <div
5
+ class="css-dpbaeg-root"
6
+ >
7
+ <h4
8
+ class="MuiTypography-root MuiTypography-h4 MuiTypography-alignCenter css-149xwtl-MuiTypography-root"
9
+ >
10
+ JBrowse 2
11
+ </h4>
12
+ <h6
13
+ class="MuiTypography-root MuiTypography-h6 MuiTypography-alignCenter css-1l8stdg-MuiTypography-root-subtitle"
14
+ />
15
+ <p
16
+ class="MuiTypography-root MuiTypography-body1 css-ahj2mt-MuiTypography-root"
17
+ >
18
+ Here are some resources to get help. Please report the version number above when asking questions. Thanks!
19
+ </p>
20
+ <ul>
21
+ <li>
22
+ <a
23
+ class="MuiTypography-root MuiTypography-inherit MuiLink-root MuiLink-underlineAlways css-1bhi0za-MuiTypography-root-MuiLink-root"
24
+ href="https://github.com/GMOD/jbrowse-components/discussions"
25
+ rel="noopener noreferrer"
26
+ target="_blank"
27
+ >
28
+ Question & answer forum
29
+ </a>
30
+ </li>
31
+ <li>
32
+ <a
33
+ class="MuiTypography-root MuiTypography-inherit MuiLink-root MuiLink-underlineAlways css-1bhi0za-MuiTypography-root-MuiLink-root"
34
+ href="https://github.com/GMOD/jbrowse-components/issues/new/choose"
35
+ rel="noopener noreferrer"
36
+ target="_blank"
37
+ >
38
+ Report a bug
39
+ </a>
40
+ </li>
41
+ <li>
42
+ <a
43
+ class="MuiTypography-root MuiTypography-inherit MuiLink-root MuiLink-underlineAlways css-1bhi0za-MuiTypography-root-MuiLink-root"
44
+ href="https://jbrowse.org/jb2/docs/user_guide"
45
+ rel="noopener noreferrer"
46
+ target="_blank"
47
+ >
48
+ User guide
49
+ </a>
50
+ </li>
51
+ <li>
52
+ <a
53
+ class="MuiTypography-root MuiTypography-inherit MuiLink-root MuiLink-underlineAlways css-1bhi0za-MuiTypography-root-MuiLink-root"
54
+ href="https://jbrowse.org/jb2/docs/"
55
+ rel="noopener noreferrer"
56
+ target="_blank"
57
+ >
58
+ Documentation
59
+ </a>
60
+ </li>
61
+ </ul>
62
+ </div>
63
+ `;