@jbrowse/plugin-config 2.3.4 → 2.4.0

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 (57) hide show
  1. package/dist/ConfigurationEditorWidget/components/BooleanEditor.d.ts +10 -0
  2. package/dist/ConfigurationEditorWidget/components/BooleanEditor.js +14 -0
  3. package/dist/ConfigurationEditorWidget/components/BooleanEditor.js.map +1 -0
  4. package/dist/ConfigurationEditorWidget/components/ConfigurationEditor.js +25 -22
  5. package/dist/ConfigurationEditorWidget/components/ConfigurationEditor.js.map +1 -1
  6. package/dist/ConfigurationEditorWidget/components/ConfigurationTextField.d.ts +5 -0
  7. package/dist/ConfigurationEditorWidget/components/ConfigurationTextField.js +19 -0
  8. package/dist/ConfigurationEditorWidget/components/ConfigurationTextField.js.map +1 -0
  9. package/dist/ConfigurationEditorWidget/components/NumberEditor.d.ts +11 -0
  10. package/dist/ConfigurationEditorWidget/components/NumberEditor.js +46 -0
  11. package/dist/ConfigurationEditorWidget/components/NumberEditor.js.map +1 -0
  12. package/dist/ConfigurationEditorWidget/components/NumberMapEditor.d.ts +11 -0
  13. package/dist/ConfigurationEditorWidget/components/NumberMapEditor.js +66 -0
  14. package/dist/ConfigurationEditorWidget/components/NumberMapEditor.js.map +1 -0
  15. package/dist/ConfigurationEditorWidget/components/SlotEditor.js +15 -97
  16. package/dist/ConfigurationEditorWidget/components/SlotEditor.js.map +1 -1
  17. package/dist/ConfigurationEditorWidget/components/StringArrayEditor.js +1 -1
  18. package/dist/ConfigurationEditorWidget/components/StringArrayEditor.js.map +1 -1
  19. package/dist/ConfigurationEditorWidget/components/StringArrayMapEditor.d.ts +14 -0
  20. package/dist/ConfigurationEditorWidget/components/StringArrayMapEditor.js +71 -0
  21. package/dist/ConfigurationEditorWidget/components/StringArrayMapEditor.js.map +1 -0
  22. package/dist/RefNameAliasAdapter/RefNameAliasAdapter.js +3 -4
  23. package/dist/RefNameAliasAdapter/RefNameAliasAdapter.js.map +1 -1
  24. package/esm/ConfigurationEditorWidget/components/BooleanEditor.d.ts +10 -0
  25. package/esm/ConfigurationEditorWidget/components/BooleanEditor.js +9 -0
  26. package/esm/ConfigurationEditorWidget/components/BooleanEditor.js.map +1 -0
  27. package/esm/ConfigurationEditorWidget/components/ConfigurationEditor.js +25 -22
  28. package/esm/ConfigurationEditorWidget/components/ConfigurationEditor.js.map +1 -1
  29. package/esm/ConfigurationEditorWidget/components/ConfigurationTextField.d.ts +5 -0
  30. package/esm/ConfigurationEditorWidget/components/ConfigurationTextField.js +13 -0
  31. package/esm/ConfigurationEditorWidget/components/ConfigurationTextField.js.map +1 -0
  32. package/esm/ConfigurationEditorWidget/components/NumberEditor.d.ts +11 -0
  33. package/esm/ConfigurationEditorWidget/components/NumberEditor.js +18 -0
  34. package/esm/ConfigurationEditorWidget/components/NumberEditor.js.map +1 -0
  35. package/esm/ConfigurationEditorWidget/components/NumberMapEditor.d.ts +11 -0
  36. package/esm/ConfigurationEditorWidget/components/NumberMapEditor.js +38 -0
  37. package/esm/ConfigurationEditorWidget/components/NumberMapEditor.js.map +1 -0
  38. package/esm/ConfigurationEditorWidget/components/SlotEditor.js +13 -95
  39. package/esm/ConfigurationEditorWidget/components/SlotEditor.js.map +1 -1
  40. package/esm/ConfigurationEditorWidget/components/StringArrayEditor.js +1 -1
  41. package/esm/ConfigurationEditorWidget/components/StringArrayEditor.js.map +1 -1
  42. package/esm/ConfigurationEditorWidget/components/StringArrayMapEditor.d.ts +14 -0
  43. package/esm/ConfigurationEditorWidget/components/StringArrayMapEditor.js +43 -0
  44. package/esm/ConfigurationEditorWidget/components/StringArrayMapEditor.js.map +1 -0
  45. package/esm/RefNameAliasAdapter/RefNameAliasAdapter.js +3 -4
  46. package/esm/RefNameAliasAdapter/RefNameAliasAdapter.js.map +1 -1
  47. package/package.json +2 -2
  48. package/src/ConfigurationEditorWidget/components/BooleanEditor.tsx +35 -0
  49. package/src/ConfigurationEditorWidget/components/ConfigurationEditor.tsx +116 -123
  50. package/src/ConfigurationEditorWidget/components/ConfigurationTextField.tsx +22 -0
  51. package/src/ConfigurationEditorWidget/components/NumberEditor.tsx +34 -0
  52. package/src/ConfigurationEditorWidget/components/NumberMapEditor.tsx +94 -0
  53. package/src/ConfigurationEditorWidget/components/SlotEditor.tsx +57 -304
  54. package/src/ConfigurationEditorWidget/components/StringArrayEditor.tsx +1 -4
  55. package/src/ConfigurationEditorWidget/components/StringArrayMapEditor.tsx +104 -0
  56. package/src/ConfigurationEditorWidget/components/__snapshots__/ConfigurationEditor.test.tsx.snap +113 -58
  57. package/src/RefNameAliasAdapter/RefNameAliasAdapter.ts +3 -4
@@ -0,0 +1,35 @@
1
+ import React from 'react'
2
+ import { observer } from 'mobx-react'
3
+
4
+ import {
5
+ Checkbox,
6
+ FormControl,
7
+ FormControlLabel,
8
+ FormHelperText,
9
+ } from '@mui/material'
10
+
11
+ export default observer(function ({
12
+ slot,
13
+ }: {
14
+ slot: {
15
+ name: string
16
+ value: boolean
17
+ set: (arg: boolean) => void
18
+ description: string
19
+ }
20
+ }) {
21
+ return (
22
+ <FormControl>
23
+ <FormControlLabel
24
+ label={slot.name}
25
+ control={
26
+ <Checkbox
27
+ checked={slot.value}
28
+ onChange={evt => slot.set(evt.target.checked)}
29
+ />
30
+ }
31
+ />
32
+ <FormHelperText>{slot.description}</FormHelperText>
33
+ </FormControl>
34
+ )
35
+ })
@@ -28,7 +28,7 @@ import { AbstractSessionModel } from '@jbrowse/core/util'
28
28
 
29
29
  const useStyles = makeStyles()(theme => ({
30
30
  expandIcon: {
31
- color: '#fff',
31
+ color: theme.palette.tertiary?.contrastText || '#fff',
32
32
  },
33
33
  root: {
34
34
  padding: theme.spacing(1, 3, 1, 1),
@@ -37,7 +37,6 @@ const useStyles = makeStyles()(theme => ({
37
37
  display: 'block',
38
38
  padding: theme.spacing(1),
39
39
  },
40
-
41
40
  accordion: {
42
41
  border: `1px solid ${theme.palette.text.primary}`,
43
42
  },
@@ -47,133 +46,127 @@ const useStyles = makeStyles()(theme => ({
47
46
  },
48
47
  }))
49
48
 
50
- const Member = observer(
51
- (props: {
52
- slotName: string
53
- slotSchema: IAnyType
54
- schema: AnyConfigurationModel
55
- slot?: AnyConfigurationModel | AnyConfigurationModel[]
56
- path?: string[]
57
- }) => {
58
- const { classes } = useStyles()
59
- const {
60
- slotName,
61
- slotSchema,
62
- schema,
63
- slot = schema[slotName],
64
- path = [],
65
- } = props
66
- let typeSelector
67
- if (isConfigurationSchemaType(slotSchema)) {
68
- if (slot.length) {
69
- return slot.map((subslot: AnyConfigurationModel, slotIndex: number) => {
70
- const key = `${singular(slotName)} ${slotIndex + 1}`
71
- return <Member {...props} key={key} slot={subslot} slotName={key} />
72
- })
73
- }
74
- // if this is an explicitly typed schema, make a type-selecting dropdown
75
- // that can be used to change its type
76
- const typeNameChoices = getTypeNamesFromExplicitlyTypedUnion(slotSchema)
77
- if (typeNameChoices.length) {
78
- typeSelector = (
79
- <TypeSelector
80
- typeNameChoices={typeNameChoices}
81
- slotName={slotName}
82
- slot={slot}
83
- onChange={evt => {
84
- if (evt.target.value !== slot.type) {
85
- schema.setSubschema(slotName, { type: evt.target.value })
86
- }
87
- }}
88
- />
89
- )
90
- }
91
- return (
92
- <Accordion defaultExpanded className={classes.accordion}>
93
- <AccordionSummary
94
- expandIcon={<ExpandMoreIcon className={classes.expandIcon} />}
95
- >
96
- <Typography>{[...path, slotName].join('➔')}</Typography>
97
- </AccordionSummary>
98
- <AccordionDetails className={classes.expansionPanelDetails}>
99
- {typeSelector}
100
- <FormGroup className={classes.noOverflow}>
101
- <Schema schema={slot} path={[...path, slotName]} />
102
- </FormGroup>
103
- </AccordionDetails>
104
- </Accordion>
49
+ const Member = observer(function (props: {
50
+ slotName: string
51
+ slotSchema: IAnyType
52
+ schema: AnyConfigurationModel
53
+ slot?: AnyConfigurationModel | AnyConfigurationModel[]
54
+ path?: string[]
55
+ }) {
56
+ const { classes } = useStyles()
57
+ const {
58
+ slotName,
59
+ slotSchema,
60
+ schema,
61
+ slot = schema[slotName],
62
+ path = [],
63
+ } = props
64
+ let typeSelector
65
+ if (isConfigurationSchemaType(slotSchema)) {
66
+ if (slot.length) {
67
+ return slot.map((subslot: AnyConfigurationModel, slotIndex: number) => {
68
+ const key = `${singular(slotName)} ${slotIndex + 1}`
69
+ return <Member {...props} key={key} slot={subslot} slotName={key} />
70
+ })
71
+ }
72
+ // if this is an explicitly typed schema, make a type-selecting dropdown
73
+ // that can be used to change its type
74
+ const typeNameChoices = getTypeNamesFromExplicitlyTypedUnion(slotSchema)
75
+ if (typeNameChoices.length) {
76
+ typeSelector = (
77
+ <TypeSelector
78
+ typeNameChoices={typeNameChoices}
79
+ slotName={slotName}
80
+ slot={slot}
81
+ onChange={evt => {
82
+ if (evt.target.value !== slot.type) {
83
+ schema.setSubschema(slotName, { type: evt.target.value })
84
+ }
85
+ }}
86
+ />
105
87
  )
106
88
  }
89
+ return (
90
+ <Accordion defaultExpanded className={classes.accordion}>
91
+ <AccordionSummary
92
+ expandIcon={<ExpandMoreIcon className={classes.expandIcon} />}
93
+ >
94
+ <Typography>{[...path, slotName].join('➔')}</Typography>
95
+ </AccordionSummary>
96
+ <AccordionDetails className={classes.expansionPanelDetails}>
97
+ {typeSelector}
98
+ <FormGroup className={classes.noOverflow}>
99
+ <Schema schema={slot} path={[...path, slotName]} />
100
+ </FormGroup>
101
+ </AccordionDetails>
102
+ </Accordion>
103
+ )
104
+ }
107
105
 
108
- if (isConfigurationSlotType(slotSchema)) {
109
- // this is a regular config slot
110
- return <SlotEditor key={slotName} slot={slot} slotSchema={slotSchema} />
111
- }
106
+ if (isConfigurationSlotType(slotSchema)) {
107
+ // this is a regular config slot
108
+ return <SlotEditor key={slotName} slot={slot} slotSchema={slotSchema} />
109
+ }
112
110
 
113
- return null
114
- },
115
- )
111
+ return null
112
+ })
116
113
 
117
- const Schema = observer(
118
- ({
119
- schema,
120
- path = [],
121
- }: {
122
- schema: AnyConfigurationModel
123
- path?: string[]
124
- }) => {
125
- const properties = getMembers(schema).properties
126
- return (
127
- <>
128
- {Object.entries(properties).map(([slotName, slotSchema]) => (
129
- <Member
130
- key={slotName}
131
- slotName={slotName}
132
- slotSchema={slotSchema}
133
- path={path}
134
- schema={schema}
135
- />
136
- ))}
137
- </>
138
- )
139
- },
140
- )
114
+ const Schema = observer(function ({
115
+ schema,
116
+ path = [],
117
+ }: {
118
+ schema: AnyConfigurationModel
119
+ path?: string[]
120
+ }) {
121
+ const properties = getMembers(schema).properties
122
+ return (
123
+ <>
124
+ {Object.entries(properties).map(([slotName, slotSchema]) => (
125
+ <Member
126
+ key={slotName}
127
+ slotName={slotName}
128
+ slotSchema={slotSchema}
129
+ path={path}
130
+ schema={schema}
131
+ />
132
+ ))}
133
+ </>
134
+ )
135
+ })
141
136
 
142
- const ConfigurationEditor = observer(
143
- ({
144
- model,
145
- }: {
146
- model: { target: AnyConfigurationModel }
147
- session?: AbstractSessionModel
148
- }) => {
149
- const { classes } = useStyles()
150
- // key forces a re-render, otherwise the same field can end up being used
151
- // for different tracks since only the backing model changes for example
152
- // see pr #804
153
- const { target } = model
154
- const key = target && readConfObject(target, 'trackId')
155
- const name = target && readConfObject(target, 'name')
156
- return (
157
- <>
158
- <Accordion key={key} defaultExpanded className={classes.accordion}>
159
- <AccordionSummary
160
- expandIcon={<ExpandMoreIcon className={classes.expandIcon} />}
161
- >
162
- <Typography>{name ?? 'Configuration'}</Typography>
163
- </AccordionSummary>
164
- <AccordionDetails
165
- className={classes.expansionPanelDetails}
166
- data-testid="configEditor"
167
- >
168
- {!target ? 'no target set' : <Schema schema={target} />}
169
- </AccordionDetails>
170
- </Accordion>
137
+ const ConfigurationEditor = observer(function ({
138
+ model,
139
+ }: {
140
+ model: { target: AnyConfigurationModel }
141
+ session?: AbstractSessionModel
142
+ }) {
143
+ const { classes } = useStyles()
144
+ // key forces a re-render, otherwise the same field can end up being used
145
+ // for different tracks since only the backing model changes for example
146
+ // see pr #804
147
+ const { target } = model
148
+ const key = target && readConfObject(target, 'trackId')
149
+ const name = target && readConfObject(target, 'name')
150
+ return (
151
+ <>
152
+ <Accordion key={key} defaultExpanded className={classes.accordion}>
153
+ <AccordionSummary
154
+ expandIcon={<ExpandMoreIcon className={classes.expandIcon} />}
155
+ >
156
+ <Typography>{name ?? 'Configuration'}</Typography>
157
+ </AccordionSummary>
158
+ <AccordionDetails
159
+ className={classes.expansionPanelDetails}
160
+ data-testid="configEditor"
161
+ >
162
+ {!target ? 'no target set' : <Schema schema={target} />}
163
+ </AccordionDetails>
164
+ </Accordion>
171
165
 
172
- {/* blank space at the bottom of screen allows scroll */}
173
- <div style={{ height: 300 }} />
174
- </>
175
- )
176
- },
177
- )
166
+ {/* blank space at the bottom of screen allows scroll */}
167
+ <div style={{ height: 300 }} />
168
+ </>
169
+ )
170
+ })
178
171
 
179
172
  export default ConfigurationEditor
@@ -0,0 +1,22 @@
1
+ import React from 'react'
2
+ import { TextField, TextFieldProps } from '@mui/material'
3
+ import { SanitizedHTML } from '@jbrowse/core/ui'
4
+
5
+ // adds ability to have html in helperText. note that FormHelperTextProps is
6
+ // div because the default is p which does not like div children
7
+ export default function ConfigurationTextField(
8
+ props: { helperText?: string } & TextFieldProps,
9
+ ) {
10
+ const { helperText } = props
11
+ return (
12
+ <TextField
13
+ {...props}
14
+ helperText={<SanitizedHTML html={helperText || ''} />}
15
+ FormHelperTextProps={{
16
+ // @ts-ignore
17
+ component: 'div',
18
+ }}
19
+ fullWidth
20
+ />
21
+ )
22
+ }
@@ -0,0 +1,34 @@
1
+ import React, { useEffect, useState } from 'react'
2
+ import { observer } from 'mobx-react'
3
+ import ConfigurationTextField from './ConfigurationTextField'
4
+
5
+ export default observer(function ({
6
+ slot,
7
+ }: {
8
+ slot: {
9
+ name?: string
10
+ value: string
11
+ description?: string
12
+ set: (val: number) => void
13
+ reset?: () => void
14
+ }
15
+ }) {
16
+ const [val, setVal] = useState(slot.value)
17
+ useEffect(() => {
18
+ const num = parseFloat(val)
19
+ if (!Number.isNaN(num)) {
20
+ slot.set(num)
21
+ } else {
22
+ slot.reset?.()
23
+ }
24
+ }, [slot, val])
25
+ return (
26
+ <ConfigurationTextField
27
+ label={slot.name}
28
+ helperText={slot.description}
29
+ value={val}
30
+ type="number"
31
+ onChange={evt => setVal(evt.target.value)}
32
+ />
33
+ )
34
+ })
@@ -0,0 +1,94 @@
1
+ import React, { useState } from 'react'
2
+ import { observer } from 'mobx-react'
3
+
4
+ import {
5
+ Card,
6
+ CardContent,
7
+ CardHeader,
8
+ FormHelperText,
9
+ IconButton,
10
+ InputAdornment,
11
+ InputLabel,
12
+ TextField,
13
+ } from '@mui/material'
14
+ import { makeStyles } from 'tss-react/mui'
15
+
16
+ // icons
17
+ import DeleteIcon from '@mui/icons-material/Delete'
18
+ import AddIcon from '@mui/icons-material/Add'
19
+ import NumberEditor from './NumberEditor'
20
+
21
+ const useStyles = makeStyles()(theme => ({
22
+ card: {
23
+ marginTop: theme.spacing(1),
24
+ },
25
+ }))
26
+
27
+ export default observer(function ({
28
+ slot,
29
+ }: {
30
+ slot: {
31
+ name: string
32
+ value: Map<string, string>
33
+ remove: (key: string) => void
34
+ add: (key: string, val: number) => void
35
+ description: string
36
+ }
37
+ }) {
38
+ const { classes } = useStyles()
39
+ const [value, setValue] = useState('')
40
+ return (
41
+ <>
42
+ <InputLabel>{slot.name}</InputLabel>
43
+ {Array.from(slot.value, ([key, val]) => (
44
+ <Card raised key={key} className={classes.card}>
45
+ <CardHeader
46
+ title={key}
47
+ action={
48
+ <IconButton onClick={() => slot.remove(key)}>
49
+ <DeleteIcon />
50
+ </IconButton>
51
+ }
52
+ />
53
+ <CardContent>
54
+ <NumberEditor
55
+ slot={{
56
+ value: val,
57
+ set: (val: number) => slot.add(key, val),
58
+ }}
59
+ />
60
+ </CardContent>
61
+ </Card>
62
+ ))}
63
+ <Card raised className={classes.card}>
64
+ <CardHeader
65
+ disableTypography
66
+ title={
67
+ <TextField
68
+ fullWidth
69
+ value={value}
70
+ placeholder="add new"
71
+ onChange={event => setValue(event.target.value)}
72
+ InputProps={{
73
+ endAdornment: (
74
+ <InputAdornment position="end">
75
+ <IconButton
76
+ disabled={value === ''}
77
+ onClick={() => {
78
+ slot.add(value, 0)
79
+ setValue('')
80
+ }}
81
+ >
82
+ <AddIcon />
83
+ </IconButton>
84
+ </InputAdornment>
85
+ ),
86
+ }}
87
+ />
88
+ }
89
+ />
90
+ </Card>
91
+ <FormHelperText>{slot.description}</FormHelperText>
92
+ </>
93
+ )
94
+ })