Skip to content

Commit cd59459

Browse files
authored
Merge pull request #30 from RNAcentral/no-react
No react
2 parents 2aaa972 + d144886 commit cd59459

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+3071
-11869
lines changed

.babelrc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
{
22
"presets": [
33
"@babel/preset-env",
4-
"@babel/preset-react"
54
]
65
}

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
coverage/
12
node_modules/
23
package-lock.json
34
.idea*

README.md

Lines changed: 43 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,138 +1,95 @@
11
# R2DT-Web
22

3-
This is an embeddable component that you can include into your website to visualise RNA secondary structures.
3+
This is an embeddable component that you can include in your website to visualise RNA secondary structures.
44

55
## How to use
66

7-
To use the latest stable version without worrying about updates, use the component's javascript package available at
7+
Only two lines of code are needed to use this widget: one line with the r2dt-web tag and another with the script
8+
for this component, for example:
9+
10+
```
11+
<r2dt-web/>
12+
<script type="text/javascript" src="/path/to/r2dt-web.js"></script>
13+
```
14+
And then the component provides a simple input box where you can paste an RNA/DNA sequence, URL or job ID.
15+
![Search input](img/search.png)
16+
17+
To use the latest stable version without worrying about updates, use the component's JavaScript package available at
818
GitHub:
919

1020
`<script type="text/javascript" src="https://rnacentral.github.io/r2dt-web/dist/r2dt-web.js"></script>`
1121

12-
If you prefer to install this package and perform the updates manually, see the [Installation](#Installation) section.
22+
If you prefer to install this package and perform the updates manually, see the [Installation](#Installation) section.
1323

14-
This tool can be used in two ways:
24+
### Other methods of use
1525

16-
**1- Allow the user to enter a sequence and search for the secondary structure**.
17-
18-
For that, you just need to insert this html tag somewhere in your html:
26+
If you already have an external URL that returns an SVG generated by R2DT, you can provide it through the
27+
`search` attribute as a JSON object.
1928

2029
```
21-
<r2dt-web />
30+
<r2dt-web search='{"url": "https://example.com/svg"}'></r2dt-web>
2231
```
2332

24-
To show some examples, use:
33+
To load an RNAcentral identifier (URS), pass it in the same way:
2534

2635
```
27-
<r2dt-web
28-
examples='[
29-
{"description": "RNA5S1-8", "sequence": "GUCUACGGCCAUACCACCCUGAACGCGCCCGAUCUCGUCUGAUCUCGGAAGCUAAGCAGGGUCGGGCCUGGUUAGUACUUGGAUGGGAGACCGCCUGGGAAUACCGGGUGCUGUAGGCUUU"},
30-
{"description": "TRT-TGT2-1", "sequence": "GGCTCCATAGCTCAGTGGTTAGAGCACTGGTCTTGTAAACCAGGGGTCGCGAGTTCGATCCTCGCTGGGGCCT"}
31-
]'
32-
/>
36+
<r2dt-web search='{"urs": "URS0000049E57"}'></r2dt-web>
3337
```
3438

35-
You can also customise some elements of this embeddable component. See what you can change [here](#layout).
36-
The example below changes the color of the buttons:
37-
38-
```
39-
<r2dt-web
40-
customStyle='{
41-
"searchButtonColor": "#007c82",
42-
"clearButtonColor": "#6c757d"
43-
}'
44-
/>
45-
```
46-
47-
For a minimal example, see [index.html](./index.html).
48-
49-
**2- Given a specific URS, show the secondary structure**
50-
51-
To show the secondary structure for a specific sequence, you need to pass the **U**nique **R**NA **S**equence
52-
identifier (URS), for example:
53-
54-
```
55-
<r2dt-web search='{"urs": "URS000044DFF6"}' />
56-
```
39+
![URS visualisation](img/urs.png)
5740

5841
Click [here](https://rnacentral.org/help#how-to-find-rnacentral-id) to see how you can find an RNAcentral identifier
5942
for an RNA sequence.
6043

61-
Obviously, you can automate this process by passing the URS as a variable, for example:
44+
If neither `url` nor `urs` is provided, the component will display a search field. You can optionally display example
45+
sequences using the `examples` attribute.
6246

6347
```
64-
<r2dt-web search='{"urs": "{{ variable }}"}' />
48+
<r2dt-web
49+
examples='[
50+
{"description": "RNA5S1-8", "sequence": "GUCUACGGCCAUACCACCCUGAACGCGCCCGAUCUCGUCUGAUCUCGGAAGCUAAGCAGGGUCGGGCCUGGUUAGUACUUGGAUGGGAGACCGCCUGGGAAUACCGGGUGCUGUAGGCUUU"},
51+
{"description": "TRT-TGT2-1", "sequence": "GGCTCCATAGCTCAGTGGTTAGAGCACTGGTCTTGTAAACCAGGGGTCGCGAGTTCGATCCTCGCTGGGGCCT"}
52+
]'
53+
/>
6554
```
6655

67-
For a minimal example, see [urs-example.html](./urs-example.html).
56+
![Search with examples](img/search-with-examples.png)
6857

6958
## Installation
7059

71-
Download this package directly from GitHub.
60+
Clone this repository from GitHub.
7261

7362
`git clone https://github.com/RNAcentral/r2dt-web.git`
7463

75-
Now you can add the component's javascript bundle (it contains all the styles and fonts) to your web page either
64+
Now you can add the component's JavaScript bundle (it contains all the styles and fonts) to your web page either
7665
directly or through an import with Webpack:
7766

7867
`<script type="text/javascript" src="/r2dt-web/dist/r2dt-web.js"></script>`
7968

80-
You will need to run the `git pull` command whenever there are updates.
81-
8269
## Attributes/parameters
8370

8471
This component accepts a number of attributes. You pass them as html attributes and their values are strings
8572
(this is a requirement of Web Components):
8673

87-
#### layout
88-
89-
Parameters that you can use to customise some elements of this embeddable component
90-
91-
| parameter | description |
92-
|-------------------|------------------------------------------------------------------------------------------|
93-
| fixCss | fix the CSS. Use *"fixCss": "true"* if the button sizes are different |
94-
| linkColor | change the color of the links |
95-
| searchButtonColor | change the color of the `Search` button |
96-
| clearButtonColor | change the color of the `Clear` button |
97-
| titleColor | change the color of the `Secondary structure` text |
98-
| titleSize | change the size of the `Secondary structure` text |
99-
| hideTitle | Use *"hideTitle": "true"* to hide the title |
100-
| legendLocation | Use *"legendLocation": "right"* to show the legend side by side with secondary structure |
101-
102-
## Developer details
103-
104-
When integrating the r2dt-web component into your webpage, please be aware that its styles might conflict
105-
with the existing styles of your page. To prevent such CSS conflicts and ensure proper isolation of the
106-
component's styles, you can add the following lines to your webpage's CSS file:
107-
108-
```
109-
r2dt-web {
110-
all: initial;
111-
}
112-
```
113-
114-
This will reset all inherited styles for the r2dt-web component, minimizing the chances of unintended
115-
style interference.
74+
| parameter | description |
75+
|-----------|------------------------------------------------------------------------|
76+
| search | JSON object with `url` or `urs` attributes |
77+
| examples | Array of example sequences with `description` and `sequence` |
78+
| legend | Legend position (left by default). Can be `left`, `right` and `center` |
11679

11780
### Local development
11881

11982
1. `npm install`
12083

121-
2. `npm run serve` to start a server on http://localhost:8080/
122-
123-
3. `npm run clean` to clean the dist folder of old assets
84+
2. `npm run update-templates` to use the latest models from the R2DT repository
12485

125-
4. `npm run build` to generate a new distribution.
86+
3. `npm start` to start a server on http://localhost:9000/
12687

127-
### Notes
88+
4. `npm run build` to generate a new distribution
12889

129-
This embed is implemented as a Web Component, wrapping a piece of code in React/Redux.
130-
The CSS styles and fonts are bundled into the javascript inline via Webpack build system, see webpack.config.js file.
131-
Upon load of r2dt-web.js, the component registers itself in the custom elements registry.
90+
5. `npm test` to run unit tests
13291

133-
Web Components accept input parameters as strings. That means that we have to parse parameters in Web Component
134-
initialization code and pass the resulting objects as props to React. Here are some examples of passing the
135-
parameters to the Web Component or from Web Component to React:
92+
### Notes
13693

137-
* https://hackernoon.com/how-to-turn-react-component-into-native-web-component-84834315cb24
138-
* https://stackoverflow.com/questions/50404970/web-components-pass-data-to-and-from/50416836
94+
- Implemented as a Web Component using pure JavaScript — no React or Redux dependencies.
95+
- If both `url` and `urs` are provided, `urs` takes precedence.

__tests__/actions.test.js

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import { onSubmit, fetchStatus, getSvg, getFasta, getTsv, fetchSvgFromUrl } from '../src/actions';
2+
import routes from '../src/routes';
3+
4+
global.fetch = jest.fn();
5+
6+
describe('actions.js', () => {
7+
beforeEach(() => jest.clearAllMocks() );
8+
9+
test('onSubmit success', async () => {
10+
fetch
11+
.mockResolvedValueOnce({ ok: true, text: () => Promise.resolve('job123') }) // submitJob
12+
.mockResolvedValueOnce({ ok: true, text: () => Promise.resolve('FINISHED') }) // jobStatus
13+
.mockResolvedValueOnce({ ok: true, text: () => Promise.resolve('>hdr\nACGU\n(((...)))') }) // fasta
14+
.mockResolvedValueOnce({ ok: true, text: () => Promise.resolve('<svg></svg>') }) // svg
15+
.mockResolvedValueOnce({ ok: true, text: () => Promise.resolve('tsv\ttemplate\tsource') }); // tsv
16+
17+
const result = await onSubmit('body=data');
18+
expect(result.jobId).toBe('job123');
19+
expect(result.svg).toContain('<svg');
20+
expect(result.tsv).toEqual({ template: 'template', source: 'source' });
21+
});
22+
23+
test('fetchStatus returns NOT_FOUND', async () => {
24+
fetch.mockResolvedValueOnce({ ok: true, text: () => Promise.resolve('NOT_FOUND') });
25+
const result = await fetchStatus('job123');
26+
expect(result).toBe('NOT_FOUND');
27+
});
28+
29+
test('fetchStatus returns FAILURE', async () => {
30+
fetch.mockResolvedValueOnce({ ok: true, text: () => Promise.resolve('FAILURE') });
31+
const result = await fetchStatus('job123');
32+
expect(result).toBe('FAILURE');
33+
});
34+
35+
test('fetchStatus returns ERROR', async () => {
36+
fetch.mockResolvedValueOnce({ ok: true, text: () => Promise.resolve('ERROR') });
37+
const result = await fetchStatus('job123');
38+
expect(result).toBe('ERROR');
39+
});
40+
41+
test('fetchStatus returns error on bad response', async () => {
42+
fetch.mockResolvedValueOnce({ ok: false, status: 500 });
43+
await expect(fetchStatus('job123')).rejects.toThrow('Error 500');
44+
});
45+
46+
test('getFasta parses correctly', async () => {
47+
fetch.mockResolvedValueOnce({
48+
ok: true,
49+
text: () => Promise.resolve('>header\nACGU\n(((...)))')
50+
});
51+
const res = await getFasta('job123');
52+
expect(res).toEqual({ fastaHeader: '>header', sequence: 'ACGU', dotBracketNotation: '(((...)))' });
53+
});
54+
55+
test('getTsv parses correctly', async () => {
56+
fetch.mockResolvedValueOnce({
57+
ok: true,
58+
text: () => Promise.resolve('col1\ttemplate\tsource')
59+
});
60+
const res = await getTsv('job123');
61+
expect(res).toEqual({ template: 'template', source: 'source' });
62+
});
63+
64+
test('getSvg returns NO_MATCH', async () => {
65+
fetch.mockResolvedValueOnce({ ok: true, status: 400 });
66+
const result = await getSvg('job123');
67+
expect(result).toBe('NO_MATCH');
68+
});
69+
70+
test('getSvg returns empty string when text is missing', async () => {
71+
fetch.mockResolvedValueOnce({
72+
ok: true,
73+
text: () => Promise.resolve('')
74+
});
75+
76+
const result = await getSvg('job123');
77+
expect(result).toBe('');
78+
});
79+
80+
test('fetchSvgFromUrl returns NO_SVG for invalid data', async () => {
81+
fetch.mockResolvedValueOnce({ ok: true, text: () => Promise.resolve('not an svg') });
82+
const res = await fetchSvgFromUrl('http://example.com');
83+
expect(res).toBe('NO_SVG');
84+
});
85+
86+
test('fetchSvgFromUrl handles network errors', async () => {
87+
fetch.mockRejectedValueOnce(new Error('network failed'));
88+
const result = await fetchSvgFromUrl('http://bad.url');
89+
expect(result).toBe('NO_SVG');
90+
});
91+
});

0 commit comments

Comments
 (0)