Compare commits

...
Author SHA1 Message Date
Mikołaj Młodzikowski b54f7d1240 feat(ci): remove extra files 2023-09-05 12:12:41 +02:00
Mikołaj Młodzikowski 149053f9e2 feat(ci): disable linting / building 2023-09-05 11:53:46 +02:00
Mikołaj Młodzikowski 6e6a4ff346 feat(ci): disable hooks 2023-09-05 11:53:02 +02:00
Mikołaj Młodzikowski 41bd7d7e53 feat(ci): deploy preview 2023-09-05 11:44:27 +02:00
Sam Keen 2a0811863f feat(explorer): add headings and anchors to net params page (#4691) 2023-09-05 09:09:31 +01:00
Sam Keen 4f610bbd1b feat(governance): vote status improvements (#4654) 2023-09-05 09:43:37 +02:00
Matthew Russell 104b2d145d chore: release v0.21.0-core-0.72.14 2023-09-04 20:05:18 -07:00
Matthew Russell f75787562c chore(trading): minor style changes 2 (#4698) 2023-09-04 20:03:59 -07:00
Matthew Russell 678c9090c7 chore(trading): update flags for 0.72.14 (#4697) 2023-09-04 18:33:58 -07:00
Matthew Russell 21bb6a69c6 chore(trading): minor style tweaks (#4696) 2023-09-04 17:21:46 -07:00
Bartłomiej Głownia 467ed5d53d feat(deal-ticket): make margin values section expandable (#4664) 2023-09-04 17:05:53 -07:00
m.rayandMatthew Russell d268088e60 chore(trading): update dropdowns (#4694)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-09-04 20:25:24 +00:00
Matthew Russell 1208d3f2a8 feat(market-depth): additional orderbook grouping levels (#4658) 2023-09-04 07:47:27 -07:00
Sam Keen b5ba4f99d9 fix(governance,utils,react-helpers): token locale formatting issue (#4678) 2023-09-04 12:09:38 +01:00
Joe Tsang 66f25603d3 test(explorer): add e2e test for explorer oracles (#4657) 2023-09-04 09:41:04 +00:00
Edd 4afd469404 chore(explorer,trading,governance): reconfigure hooks (#4655) 2023-09-04 09:36:13 +00:00
Maciek 9e5bc9c8d1 chore(trading): handle negative decimals (#4659) 2023-09-04 09:46:02 +02:00
Matthew Russell e41aff88b1 chore(trading): make top traded default sort for market selector (#4637) 2023-09-03 11:15:41 -07:00
Matthew Russell 9209074332 chore(trading): change pane context buttons color (#4680) 2023-09-01 17:00:11 -07:00
Art 39e5836edb fix(wallet): check if snaps are supported (#4671) 2023-09-01 11:22:13 +00:00
Art 85a4981700 fix(proposals): protocol upgrade notification block querying (#4665) 2023-09-01 12:30:01 +02:00
Ben 247927e939 chore(trading): get started specs update (#4681) 2023-09-01 10:55:56 +01:00
Maciek 6523490d96 chore(trading): 4349 delayed telemetry opt in (#4642) 2023-09-01 09:00:20 +00:00
daro-maj 105a758e8d test(trading): add stop order oco spec (#4669) 2023-09-01 10:48:38 +02:00
Matthew Russell 559ef48d6d feat(trading): orderbook changes (#4652) 2023-08-31 13:34:13 -07:00
Bartłomiej Głownia 255c3752f2 feat(deal-ticket): fix flaky tests (#4667) 2023-08-31 10:41:58 -07:00
Joe Tsang 2bbf1e2b81 chore(ci): update config.hcl file (#4677) 2023-08-31 10:35:46 -07:00
Maciek 06d3ef8d73 fix: de-polish and fix links to mozilla (#4670) 2023-08-31 14:09:41 +00:00
ArtandDariusz Majcherczyk de4c7926c2 chore(deposits): formatting tweaks (#4648)
Co-authored-by: Dariusz Majcherczyk <dariusz.majcherczyk@gmail.com>
2023-08-31 15:30:03 +02:00
Ben 50959b4c50 chore(trading): delete closed market tests (#4660) 2023-08-31 13:15:02 +01:00
Bartłomiej Głownia a8c2f4e025 feat(deal-ticket): update deal ticket submit buttons (#4635) 2023-08-31 11:54:52 +02:00
Matthew Russell 2cea73c567 chore(wallet): prepare wallet lib for publishing (#4638) 2023-08-30 19:40:04 -07:00
Edd b0a54be408 fix(explorer): format iceberg size according to market (#4612) 2023-08-30 09:39:49 +01:00
Matthew Russell d1a036e53c chore(trading,governance,explorer): update pr template to not close issues (#4645) 2023-08-29 08:54:09 -07:00
MaciekandMatthew Russell 9ac199f59a chore(ledger): remove ledger entries table, adjust download form (#4611)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-08-29 06:57:02 -07:00
Edd 332cc302c3 feat(explorer): add stop order view (#4418) 2023-08-29 12:24:22 +02:00
Edd 4da0e9c368 fix(explorer): correctly display data source matches (#4631) 2023-08-29 12:23:55 +02:00
Joe Tsang 13e1f99767 test(governance): successor market proposal changes (#4632) 2023-08-29 09:47:59 +01:00
ArtandMatthew Russell 5c18c898b0 feat(wallet): metamask snaps (#4621)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-08-28 18:51:58 -07:00
Matthew Russell 4fe81cc4aa fix(trading): update fonts for pennant (#4636) 2023-08-28 10:02:32 -07:00
Art 0fb8ee3abb fix(proposals): parsing block duration value (#4613) 2023-08-28 13:11:24 +00:00
Matthew Russell 3422b99491 feat(trading): change to system monospace font (#4634) 2023-08-25 15:24:57 -07:00
Radosław Szpiech 287e294281 chore(trading): remove market-all-cy.ts as it was moved to console-test (#4633) 2023-08-25 14:28:42 -07:00
Bartłomiej GłowniaandMatthew Russell dc959025c6 fix(deal-ticket): disable iceberg for IOC and FOK (#4629)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-08-25 14:27:55 -07:00
Ben 63bfcc8f65 chore(trading): remove deal ticket basic submit (#4628) 2023-08-25 10:57:09 -07:00
Bartłomiej Głownia 952e906eac fix(deal-ticket): show expiresAt field value from state only, add required validation (#4626) 2023-08-25 10:56:43 -07:00
Matthew Russell e765c247ef chore(trading): adjust the get started checkboxes slightly (#4622) 2023-08-25 10:52:52 -07:00
Sam KeenandJoe 52dea6d0dc feat(ui-toolkit,governance): description preview and read more pattern (#4599)
Co-authored-by: Joe <joe@vega.xyz>
2023-08-25 16:46:03 +01:00
Ben 97f243e5f7 chore(trading): show test run time (#4623) 2023-08-25 11:04:38 +01:00
daro-maj 9992d9f053 test(trading): remove markets proposed tests (#4618) 2023-08-25 12:00:04 +02:00
Sam Keen 3e26431e8f feat(governance): small UX improvements to successor market panels (#4562) 2023-08-25 09:58:01 +00:00
Bartłomiej Głownia 6a9f15f59e feat(deal-ticket): submit oco stop orders (#4539) 2023-08-25 08:37:14 +02:00
Edd 4684745382 fix(explorer): properly render issue signatures tx (#4610) 2023-08-24 18:51:55 +01:00
Joe Tsang 927e21b045 test(explorer): add e2e test for asset proposal on explorer (#4616) 2023-08-24 18:51:24 +01:00
ArtandMatthew Russell 570472b739 feat(trading): sort by top traded in market selector (#4574)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-08-24 11:05:09 +02:00
Maciek 28f7bd36e7 feat(trading): 4351 get started steps enhancement (#4565) 2023-08-24 10:03:56 +02:00
Radosław Szpiech 0f3e5595ba chore(trading): add dist loadfile to console-test run (#4609) 2023-08-23 13:13:33 +00:00
Bartłomiej Głownia d3dbdd2bd5 feat(trading): view stop order history (#4586) 2023-08-23 13:41:56 +02:00
daro-majandJoe Tsang 0767139712 test(trading): update cypress to version 12.17.0 (#4575)
Co-authored-by: Joe Tsang <30622993+jtsang586@users.noreply.github.com>
2023-08-23 13:34:10 +02:00
daro-maj 250492a02c test(trading): tests for distinguish between product types (#4589) 2023-08-23 11:06:16 +02:00
Maciek 29f3374c61 fix(trading): fix hidden sidebars on load (#4583) 2023-08-23 07:23:36 +00:00
Ciaran McGhie ba7b574a07 chore(ui-toolkit,react-helpers,announcements): bump versions add react react-dom to peer deps (#4602) 2023-08-22 18:51:13 +01:00
Art afd8650657 fix(proposals): error policy guard for proposal data provider (#4573) 2023-08-22 17:58:01 +01:00
Bartłomiej GłowniaandMatthew Russell 78414b4429 feat(ui-toolkit): form element design changes (#4525)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-08-22 17:39:52 +01:00
m.ray e0a91b3850 chore(trading): revert metadata update - viewport meta tags should not be used in _document.js's (#4591) 2023-08-22 17:23:24 +01:00
Joe Tsang 80f7a08765 test(governance): vote error test (#4588) 2023-08-22 16:15:33 +01:00
Edd 3e627ff849 fix(explorer): check linking type when summing stake (#4585) 2023-08-22 13:08:00 +00:00
Joe Tsang d2854b6e90 chore(governance): add acs for network nodes (#4577) 2023-08-22 12:06:41 +01:00
Matthew Russell 6d130c9cfc feat(positions): filter closed markets in positions table (#4569) 2023-08-22 10:17:10 +01:00
Ben ab4f4e9084 chore(trading): remove trading chart tests (#4582) 2023-08-22 08:29:47 +01:00
Ben fa1825ca64 chore(trading): remove settings test (#4580) 2023-08-22 08:29:13 +01:00
Edd 206c6c7207 fix(explorer): check linking type when summing stake 2023-08-21 18:38:32 +01:00
Sam Keen 5b0bd69710 fix(governance): better vote error handling (#4579) 2023-08-21 10:05:21 +01:00
daro-maj 546b710093 test(trading): split and skip deal ticket account tests (#4578) 2023-08-21 09:49:27 +02:00
Joe Tsang afbf62be84 chore(governance): add assertion for vote change (#4571) 2023-08-18 18:15:55 +03:00
MaciekandMatthew Russell ef26d03d36 chore(markets): distinguish between product types - add specs (#4564)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-08-17 17:27:15 +01:00
Ben 7471116ebf chore(trading): remove market selector test (#4572) 2023-08-17 17:08:27 +01:00
Matthew Russell 7142fc956c chore(positions): spec updates for positions table changes (#4568) 2023-08-17 13:07:14 +01:00
Matthew Russell 611f8d7491 feat(positions): table layout changes (#4558) 2023-08-17 11:07:48 +01:00
Sam Keen f3b72b894e feat(explorer): view 'Cancel Stop Order' Transactions (#4521) 2023-08-17 10:27:47 +01:00
daro-maj 140f16f637 test(trading): adjust tests edit and cancel orders (#4563) 2023-08-17 09:10:28 +01:00
Sam Keen cae1fc67a4 fix(governance): validators display fixes (#4547) 2023-08-16 22:58:14 +01:00
m.ray 35e25d9a8f chore(trading): metadata console updates (#4549) 2023-08-16 17:06:56 +01:00
Sam Keen 86e122389c fix(governance): proposal vote status header component (#4561) 2023-08-16 16:53:32 +01:00
Joe Tsang 6616aceebb test(governance): add timeout and fix withdrawal test (#4554) 2023-08-16 16:53:24 +01:00
ArtandMatthew Russell c937a9df0c feat(proposals): upgrade in progress notification (#4509)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-08-16 15:50:38 +01:00
MaciekandMatthew Russell 9fcf286e4b feat(trading): distinguish between product types (#4543)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-08-16 15:43:23 +01:00
m.ray e3ed71bec4 chore(trading): use action icon buttons (#4550) 2023-08-16 15:38:19 +01:00
daro-maj 809f149df9 test(trading): remove charts depth test (#4557) 2023-08-16 10:20:15 +02:00
Matthew Russell e4eedf5ccd feat(trading,datagrid): datagrid improvements (#4524) 2023-08-15 12:26:13 +01:00
Ben 20cbcb8302 chore(trading): remove home tests (#4551) 2023-08-14 18:06:36 +01:00
Joe Tsang cd3acf8ff9 test(explorer): successor market explorer (#4544) 2023-08-14 16:17:29 +01:00
Ben 30735388e8 chore(trading): remove asset tests (#4548) 2023-08-14 15:35:24 +01:00
Ben 478c4f83bb chore(trading): remove markets test (#4545) 2023-08-14 12:20:53 +01:00
JonRay15 431fab8824 chore(trading,governance): update feature-epic.md (#4528) 2023-08-14 09:43:00 +01:00
m.ray 83b6d3d295 feat(trading): metadata console (#4515) 2023-08-11 16:12:43 +00:00
Matthew Russell 98fd79bc35 fix(trading): prevent overflow in deal ticket if market fails to load (#4535) 2023-08-11 17:03:17 +01:00
Ben 39aa27f332 chore(trading): remove iceberg side order details (#4540) 2023-08-11 16:33:34 +01:00
Sam Keen bf4fb29fe6 fix(governance): fixes settlement oracle display in governance (#4534) 2023-08-11 10:08:31 +01:00
Maciek 5ed1befc53 fix(trading): remove redundant connect wallet button (#4538) 2023-08-11 10:34:32 +02:00
m.ray 1b311a8b36 fix(trading): order single view side (#4526) 2023-08-11 09:12:13 +01:00
Matthew Russell 70c3bc8128 chore(trading): remove floating buttons (#4512) 2023-08-10 16:02:46 +01:00
Joe Tsang 7e49836875 chore(governance): add e2e for successor market diff (#4519) 2023-08-10 15:03:23 +01:00
Maciek d624957e6d chore(trading): 4384 all markets in switcher (#4516) 2023-08-10 14:25:13 +01:00
Maciekandasiaznik 069b57d4ae feat(wallet): 4370 new connect wallet modal (#4492)
Co-authored-by: asiaznik <artur@vegaprotocol.io>
2023-08-10 13:03:53 +02:00
Mikołaj Młodzikowski 6894fe1264 fix(ci): hardcode env name 2023-08-10 12:53:35 +02:00
Edd 93e4b5fdb9 feat(explorer): successor markets (#4522) 2023-08-10 09:51:44 +01:00
Maciek 1566fc3984 chore: fulfill spec to 4323 (#4520) 2023-08-10 09:20:26 +02:00
Ben 39962a0246 chore(trading): update console-test runners (#4513) 2023-08-09 14:18:24 +01:00
Sam KeenandJoe 8d31510d5e feat(governance,markets,ui-toolkit): highlight successor market changes (#4449)
Co-authored-by: Joe <joe@vega.xyz>
2023-08-09 14:05:37 +01:00
Bartłomiej Głownia 26d5a67604 feat(trading): use custom toast content for stop order related transactions (#4476) 2023-08-09 14:47:10 +02:00
Bartłomiej Głownia 9cc8f5a377 feat(trading): use instrument code instead of name in positions, fills and accounts breakdown tables (#4478) 2023-08-09 12:12:07 +02:00
Matthew Russell 2e11cf4dfa fix(trading): fix persitance of resizable grid top section (#4508) 2023-08-09 11:04:32 +01:00
Bartłomiej Głownia 9e4ba9f275 feat(trading): merge deal ticket stores (#4494) 2023-08-09 11:54:16 +02:00
m.ray aac0c25d09 fix(trading): positions tooltips update (#4504) 2023-08-08 13:27:17 +01:00
Art d20c2a08ae chore(specs): succession line specs (#4505) 2023-08-08 11:23:28 +01:00
daro-maj 4e6ba5fe3d test(trading): update cypress tests (#4503) 2023-08-08 10:36:10 +02:00
Art 276d1d7c7b feat(governance): view as button (#4501) 2023-08-08 09:50:19 +02:00
m.ray 4990c5808d fix(trading): order iceberg fills (#4502) 2023-08-07 16:07:24 +00:00
Art cdd91c24f2 feat(markets): succession line (#4491) 2023-08-07 11:46:03 +00:00
Sam Keen e3eb13ca72 fix(explorer): footer visual tweaks (#4499) 2023-08-07 11:54:46 +01:00
Matthew Russell 3f0ebbf33d fix(trading): dont render get started if connected (#4490) 2023-08-07 11:35:55 +01:00
Matthew Russell 026aa3964b chore(trading): update pennant (#4493) 2023-08-07 11:06:49 +01:00
daro-maj aa4c5a4a57 test(trading): update cypress tests (#4496) 2023-08-07 10:28:13 +02:00
Matthew Russell 0874314d2a chore: add specs (#4497) 2023-08-07 08:41:25 +01:00
Matthew Russell a70a7bcc8e fix(trading): dont show connect wallet message in deal ticket (#4482) 2023-08-04 11:09:43 +01:00
Matthew Russell 4461a6a497 fix(trading): remove hover state from disabled view as button (#4487) 2023-08-04 11:08:48 +01:00
Matthew Russell 1041864ad8 chore(trading): remove unnecessary forwarding of grid refs (#4486) 2023-08-04 11:08:28 +01:00
Matthew Russell 015e0188ec chore: enable iceberg orders 2023-08-03 12:38:51 +01:00
Matthew Russell 228236c152 chore(deal-ticket): type dropdown icon (#4468) 2023-08-03 12:15:21 +01:00
Art cd6e906dc6 feat(trading): view as button in sidebar (#4470) 2023-08-03 12:14:35 +01:00
Maciek 8a47a92dbd fix(trading): add info splashes on deposit and withdrawals list containers (#4479) 2023-08-03 12:01:42 +01:00
Matthew Russell af14c27df9 chore(trading): responsive welcome dialog content and content update (#4475) 2023-08-03 12:01:09 +01:00
Matthew Russell 38edcd88c0 chore(trading): fix alignment of expire (#4477) 2023-08-03 12:00:12 +01:00
m.ray a74cf02030 feat(trading): order details dialog (#4281) 2023-08-03 10:05:12 +00:00
Joe Tsang 6686755763 chore(governance): fix failing tests from nightly run (#4453) 2023-08-03 10:01:00 +01:00
Joe Tsang 79cf8163be chore(governance): add e2e test for successor market (#4471) 2023-08-03 09:54:51 +01:00
Matthew Russell 1688e26f4b feat(trading): add fairground texture background to logo (#4474) 2023-08-03 09:28:01 +01:00
Art 5e101a8eaf feat(trading): deposit page (#4472) 2023-08-02 19:06:49 +02:00
Matthew Russell 65071f57d0 fix(trading,governance): eager connection for browser wallet (#4444) 2023-08-02 17:16:09 +01:00
daro-maj b87c981a90 test(trading): fix for key-to-key test (#4469) 2023-08-02 18:11:31 +02:00
Joe TsangandMatthew Russell b509e5b347 test(explorer): e2e proposal test (#4379)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-08-02 16:37:55 +01:00
Maciek bb9fce4831 feat(trading): 4322 get started steps (#4440) 2023-08-02 15:34:04 +00:00
Matthew Russell 918825aca8 chore: adjust spacing and text size of long/short type selectors 2023-08-02 15:36:39 +01:00
Matthew Russell 74bf183fad feat(trading): 3114 pane context (#4442) 2023-08-02 15:29:41 +01:00
m.ray 674baa1ee3 fix(trading): iceberg order fills (#4460) 2023-08-02 16:55:49 +03:00
Matthew Russell d18a0c7e15 fix(trading): lp table forward ref breaking resize grid (#4447) 2023-08-02 13:59:05 +01:00
m.ray 76a6005b77 fix(trading): replace all markets with open markets (#4452) 2023-08-02 13:58:26 +01:00
JonRay15andMatthew Russell 79414614c2 chore(trading,governance,explorer): fix issue template (#4459)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-08-02 12:52:51 +01:00
m.ray fde49e5446 fix(trading): order stopped misleading message (#4455) 2023-08-02 11:52:07 +01:00
JonRay15andMatthew Russell 45283161aa chore: create feature-epic issue template (#4456)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-08-02 11:48:23 +01:00
Bartłomiej GłowniaandDariusz Majcherczyk 375d447fa8 feat(trading): add stop orders table and form (#4265)
Co-authored-by: Dariusz Majcherczyk <dariusz.majcherczyk@gmail.com>
2023-08-02 11:28:33 +01:00
Mikołaj Młodzikowski 1dd97a2bce feat(ci): add develop docker image (#4458) 2023-08-02 10:58:17 +02:00
Edd d4a1a9b193 fix(explorer): add tests and fix transactions url generation (#4454) 2023-08-01 16:35:40 +01:00
Edd 376c5241a8 feat(explorer): improve transaction pagination (#4435) 2023-08-01 14:41:16 +00:00
Ciaran McGhie 8e6d8517cf feat(ui-toolkit,tailwindcss-config): healthbar auction level indicator (#4406) 2023-08-01 09:44:03 +01:00
Matthew Russell 5f9ec222c1 feat(trading): navigation (#4375) 2023-07-31 17:08:55 +01:00
Edd 8954c41c0a fix(governance): remove incorrect individual reward percentage (#4443) 2023-07-31 15:23:52 +00:00
Mikołaj Młodzikowski 4414ab4f47 feat(ci): wait for publish job to complete via github API 2023-07-31 15:04:34 +02:00
Mikołaj Młodzikowski d1d4bacc68 feat(ci): patch mainnet release 2023-07-31 13:09:00 +02:00
Joe Tsang f476688c7f chore(governance): fix failing proposal test (#4434) 2023-07-31 09:58:52 +01:00
Sam Keen 8845222700 feat(governance): remove max limit on associate function (#4427) 2023-07-28 11:12:29 +00:00
Radosław Szpiech e64c464091 chore(trading): move cumulative vol testid in orderbook (#4420) 2023-07-28 11:15:40 +02:00
Joe Tsang b2c2d0d7d6 test(governance): add e2e test and acs for proposal enhancements (#4421) 2023-07-28 09:34:39 +01:00
Joe Tsang 1fb61d313c chore(governance): add acs for network upgrade banner and estimate (#4402) 2023-07-28 09:34:30 +01:00
Mikołaj Młodzikowski ccf5ff632c feat(ci): setup new tests for console (#4400) 2023-07-28 07:32:07 +02:00
Maciek 54d6aaf56f feat(trading): add market successor proposal banner (#4401) 2023-07-27 14:54:00 +00:00
Mikołaj Młodzikowski 736b262947 fix(ci): publish of static dist (#4414) 2023-07-27 13:52:00 +02:00
Sam KeenandMatthew Russell 6a07127185 feat(governance): diff highlighter for update market proposals (#4260)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-07-27 11:26:32 +01:00
Sam Keen 45f0ba7c0e fix(explorer): explorer validator page filtering (#4354) 2023-07-27 09:04:54 +00:00
John WalleyandMatthew Russell 6f4ea68778 chore(trading): upgrade pennant to v1.10.0 (#4331)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-07-26 15:35:57 +01:00
Mikołaj Młodzikowski 90dda20891 feat(ci): migrate ci-cd trigger to python (#4330) 2023-07-26 16:08:57 +02:00
Art 278eb01c2f feat(governance): successor market proposal details (#4393) 2023-07-26 14:52:12 +02:00
Sam Keen c3157de146 fix(governance): removed icons test from proposal list item details (#4392) 2023-07-25 15:59:58 +00:00
d28db485c5 chore(trading): market relationship - parent market in proposals list (#4339)
Co-authored-by: asiaznik <artur@vegaprotocol.io>
Co-authored-by: Edd <edd@vega.xyz>
2023-07-25 15:32:40 +00:00
Maciek caa0076396 fix(deal-ticket): 4046 wrong margin requirement in order (#4378) 2023-07-25 14:52:32 +00:00
Edd 451da40ed2 feat(explorer): iceberg details on order tx (#4380) 2023-07-25 13:34:04 +00:00
Joe Tsang 47694e7b72 chore(explorer): explorer market e2e test (#4355) 2023-07-25 11:25:37 +01:00
Sam Keen e01fb7f9ae feat(governance): proposal list tile and summary enhancements (#4326) 2023-07-25 11:23:25 +01:00
Joe Tsang ffcdfb6a6a chore(governance): add e2e test for network upgrade banner (#4361) 2023-07-25 10:48:59 +01:00
Joe Tsang 86f0d013bb chore(governance): minor fixes to flaky nightly tests (#4335) 2023-07-25 10:48:49 +01:00
ArtandEdd c12ac45890 feat(environment): feature flags (#4343)
Co-authored-by: Edd <edd@vega.xyz>
2023-07-25 11:12:53 +02:00
47ce68455a chore(trading): fix flakey trading-trades test (#4363)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
Co-authored-by: Art <artur@vegaprotocol.io>
Co-authored-by: Bartłomiej Głownia <bglownia@gmail.com>
Co-authored-by: Dariusz Majcherczyk <dariusz.majcherczyk@gmail.com>
2023-07-24 16:41:44 +01:00
276 changed files with 3090 additions and 5272 deletions
+28
View File
@@ -0,0 +1,28 @@
---
name: Feature Epic
about: A template to capture and scope user requirements, high level process, and basic mockups for an upcoming feature as part of the initial core spec review process.
title: 'FEATURE EPIC: '
labels: feature-epic
---
## Core Feature
<Name>
## Tasks
- [ ] Define high level requirements
- [ ] Create basic mockups
- [ ] Update "API Requirements" in core spec
- [ ] Update "User-Interface Spec" in relevant front end repo
- [ ] Create detailed user stories using normal template
## High Level Requirements
## Basic Mockups
## Link to API Requirements in Core spec
## Link to User Interface Specs
## Linked User Stories
+1 -1
View File
@@ -1,6 +1,6 @@
# Related issues 🔗 # Related issues 🔗
Closes #[Issue number here] Issue: #[Issue number here]
# Description # Description
@@ -7,7 +7,7 @@ on:
jobs: jobs:
after-release: after-release:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
timeout-minutes: 30 timeout-minutes: 45
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v3
@@ -30,18 +30,20 @@ jobs:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Wait for publish to complete
uses: lewagon/wait-on-check-action@v1.3.1
with:
ref: ${{ github.event.release.tag_name }}
check-name: '(CD) publish dist / trading'
repo-token: ${{ secrets.GITHUB_TOKEN }}
wait-interval: 10
- name: resolve ipfs hashes for release - name: resolve ipfs hashes for release
run: | run: |
echo "Name: ${{ github.event.release.name }}" echo "Name: ${{ github.event.release.name }}"
echo "Description: ${{ github.event.release.body }}" echo "Description: ${{ github.event.release.body }}"
echo "Tag: ${{ github.event.release.tag_name }}" echo "Tag: ${{ github.event.release.tag_name }}"
commit="$(git rev-list -n 1 ${{ github.event.release.tag_name }})" docker run --rm vegaprotocol/trading:mainnet cat /ipfs-hash > ipfs-hash
echo "Commit: $commit"
until docker pull vegaprotocol/trading:$commit; do
echo "Image not pushed yet, waiting 60 seconds"
sleep 60
done
docker run --rm vegaprotocol/trading:$commit cat /ipfs-hash > ipfs-hash
curl -L https://dist.ipfs.tech/kubo/v0.20.0/kubo_v0.20.0_linux-amd64.tar.gz -o kubo.tgz curl -L https://dist.ipfs.tech/kubo/v0.20.0/kubo_v0.20.0_linux-amd64.tar.gz -o kubo.tgz
tar -xzf kubo.tgz tar -xzf kubo.tgz
export PATH="$PATH:$PWD/kubo" export PATH="$PATH:$PWD/kubo"
+30 -125
View File
@@ -81,140 +81,36 @@ jobs:
with: with:
main-branch-name: develop main-branch-name: develop
- name: Check formatting
run: yarn nx format:check
- name: Lint affected
run: yarn nx affected:lint --max-warnings=0
- name: Build affected spec
run: yarn nx affected --target=build-spec
- name: Test affected
run: yarn nx affected:test
- name: Build affected
run: yarn nx affected:build || (yarn install && yarn nx affected:build)
# See affected apps # See affected apps
- name: See affected apps - name: See affected apps
run: | run: |
affected="$(yarn nx print-affected --base=${{ env.NX_BASE }} --head=${{ env.NX_HEAD }} --select=projects)"
branch_slug="$(echo '${{ github.head_ref || github.ref_name }}' | sed -r s/[^a-zA-Z0-9]+/-/g | sed -r s/^-+\|-+$//g | cut -c 1-50 )" branch_slug="$(echo '${{ github.head_ref || github.ref_name }}' | sed -r s/[^a-zA-Z0-9]+/-/g | sed -r s/^-+\|-+$//g | cut -c 1-50 )"
python3 tools/ci/check-affected.py --github-ref="${{ github.ref }}" --branch-slug="$branch_slug" --event-name="${{ github.event_name }}"
echo ">>>> debug" - name: Verify script result
echo "NX_BASE: ${{ env.NX_BASE }}" run: |
echo "NX_HEAD: ${{ env.NX_HEAD }}" echo "Check outputs from script"
echo "Affected: ${affected}" echo "projects: ${{ env.PROJECTS }}"
echo "Branch slug: ${branch_slug}" echo "projects-e2e: ${{ env.PROJECTS_E2E }}"
echo "Current ref: ${{ github.ref }}" echo "preview_governance: ${{ env.PREVIEW_GOVERNANCE }}"
echo ">>>> eof debug" echo "preview_trading: ${{ env.PREVIEW_TRADING }}"
echo "preview_explorer: ${{ env.PREVIEW_EXPLORER }}"
echo "preview_tools: ${{ env.PREVIEW_TOOLS }}"
projects_array=() # - name: Check formatting
# run: yarn nx format:check
preview_governance="not deployed" # - name: Lint affected
preview_trading="not deployed" # run: yarn nx affected:lint --max-warnings=0
preview_explorer="not deployed"
preview_tools="not deployed"
# parse if affected is any of three main applications, if none - use all of them # - name: Build affected spec
if echo "$affected" | grep -q governance; then # run: yarn nx affected --target=build-spec
echo "Governance is affected"
projects_array+=("governance")
preview_governance=$(printf "https://%s.%s.vega.rocks" "governance" "$branch_slug")
fi
if echo "$affected" | grep -q trading; then
echo "Trading is affected"
projects_array+=("trading")
preview_trading=$(printf "https://%s.%s.vega.rocks" "trading" "$branch_slug")
fi
if echo "$affected" | grep -q explorer; then
echo "Explorer is affected"
projects_array+=("explorer")
preview_explorer=$(printf "https://%s.%s.vega.rocks" "explorer" "$branch_slug")
fi
if [[ ${#projects_array[@]} -eq 0 ]]; then
projects_array=("governance" "trading" "explorer")
preview_governance=$(printf "https://%s.%s.vega.rocks" "governance" "$branch_slug")
preview_trading=$(printf "https://%s.%s.vega.rocks" "trading" "$branch_slug")
preview_explorer=$(printf "https://%s.%s.vega.rocks" "explorer" "$branch_slug")
fi
# applications parsed before this loop are applicable for running e2e-tests # - name: Test affected
projects_e2e_array=() # run: yarn nx affected:test
for project in "${projects_array[@]}"; do
projects_e2e_array+=("${project}-e2e")
done
# all applications below this loop are not applicable for running e2e-test
# check if pull request event to deploy tools # - name: Build affected
if [[ "${{ github.event_name }}" = "pull_request" ]]; then # run: yarn nx affected:build || (yarn install && yarn nx affected:build)
if echo "$affected" | grep -q multisig-signer; then
echo "Tools are affected"
echo "Deploying tools on preview"
preview_tools=$(printf "https://%s.%s.vega.rocks" "tools" "$branch_slug")
projects_array+=("multisig-signer")
fi
# those apps deploy only from develop to mainnet
elif [[ "${{ github.ref }}" =~ .*develop$ ]]; then
if echo "$affected" | grep -q multisig-signer; then
echo "Tools are affected"
echo "Deploying tools on s3"
projects_array+=("multisig-signer")
fi
if echo "$affected" | grep -q static; then
echo "static is affected"
echo "Deploying static on s3"
projects_array+=("static")
fi
if echo "$affected" | grep -q ui-toolkit; then
echo "ui-toolkit is affected"
echo "Deploying ui-toolkit on s3"
projects_array+=("ui-toolkit")
fi
fi
# if branch starts with release/ and ends with trading / governance or explorer - overwrite the array of affected projects with fixed single application
if [[ "${{ github.ref }}" == *release* ]]; then
echo ">> This is a relase branch"
case "${{ github.ref }}" in
*trading)
echo ">> Only trading will be deployed"
projects_array=(trading)
projects_e2e_array=(trading)
;;
*governance)
echo ">> Only governance will be deployed"
projects_array=(governance)
projects_e2e_array=(governance)
;;
*explorer)
echo ">> Only explorer will be deployed"
projects_array=(explorer)
projects_e2e_array=(explorer)
;;
*)
echo ">> All apps will be deployed"
;;
esac
fi
echo "Projects: ${projects_array[@]}"
echo "Projects E2E: ${projects_e2e_array[@]}"
projects_json=$(jq -M --compact-output --null-input '$ARGS.positional' --args -- "${projects_array[@]}")
projects_e2e_json=$(jq -M --compact-output --null-input '$ARGS.positional' --args -- "${projects_e2e_array[@]}")
echo PROJECTS_E2E=$projects_e2e_json >> $GITHUB_ENV
echo PROJECTS=$projects_json >> $GITHUB_ENV
echo PREVIEW_GOVERNANCE=$preview_governance >> $GITHUB_ENV
echo PREVIEW_TRADING=$preview_trading >> $GITHUB_ENV
echo PREVIEW_EXPLORER=$preview_explorer >> $GITHUB_ENV
echo PREVIEW_TOOLS=$preview_tools >> $GITHUB_ENV
outputs: outputs:
projects: ${{ env.PROJECTS }} projects: ${{ env.PROJECTS }}
@@ -224,6 +120,15 @@ jobs:
preview_explorer: ${{ env.PREVIEW_EXPLORER }} preview_explorer: ${{ env.PREVIEW_EXPLORER }}
preview_tools: ${{ env.PREVIEW_TOOLS }} preview_tools: ${{ env.PREVIEW_TOOLS }}
console-e2e:
needs: lint-test-build
name: '(CI) console python'
uses: ./.github/workflows/console-test-run.yml
secrets: inherit
if: ${{ contains(fromJSON(needs.lint-test-build.outputs.projects), 'trading') && github.event_name == 'pull_request' }}
with:
github-sha: ${{ github.event.pull_request.head.sha || github.sha }}
cypress: cypress:
needs: lint-test-build needs: lint-test-build
name: '(CI) cypress' name: '(CI) cypress'
@@ -232,7 +137,7 @@ jobs:
secrets: inherit secrets: inherit
with: with:
projects: ${{ needs.lint-test-build.outputs.projects-e2e }} projects: ${{ needs.lint-test-build.outputs.projects-e2e }}
tags: '@smoke @regression' tags: '@smoke'
publish-dist: publish-dist:
needs: lint-test-build needs: lint-test-build
+110 -27
View File
@@ -1,55 +1,138 @@
name: console-test-run name: (CI) Console tests
on: on:
workflow_call: workflow_call:
inputs: inputs:
github-sha: github-sha:
required: true required: true
type: string type: string
jobs: jobs:
console-test: run-tests:
timeout-minutes: 5 name: run-tests
runs-on: self-hosted-runner runs-on: 8-cores
timeout-minutes: 20
steps: steps:
#----------------------------------------------
# check-out frontend-monorepo
#----------------------------------------------
- name: Checkout console test repo
uses: actions/checkout@v3
with:
ref: ${{ inputs.github-sha }}
#----------------------------------------------
# cache node modules
#----------------------------------------------
- name: Cache node modules
id: cache
uses: actions/cache@v3
with:
path: node_modules
key: ${{ runner.os }}-cache-node-modules-${{ hashFiles('yarn.lock') }}
# comment out "restore-keys" if you need to rebuild yarn from 0
restore-keys: |
${{ runner.os }}-cache-node-modules-
#----------------------------------------------
# setup node
#----------------------------------------------
- name: Setup node
uses: actions/setup-node@v3
with:
node-version-file: '.nvmrc'
# https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions
cache: yarn
#----------------------------------------------
# install deps if cache missing
#----------------------------------------------
- name: yarn install
if: steps.cache.outputs.cache-hit != 'true'
run: yarn install --pure-lockfile
#----------------------------------------------
# build trading
#----------------------------------------------
- name: Build affected spec
run: |
yarn env-cmd -f ./apps/trading/.env.stagnet1 yarn nx export trading
#----------------------------------------------
# run trading server
#----------------------------------------------
- name: Run trading server
run: |
docker run -d -p 80:4200 -v $PWD/docker/nginx.conf:/etc/nginx/conf.d/default.conf -v $PWD/dist/apps/trading/exported:/usr/share/nginx/html nginx:1.23-alpine@sha256:6318314189b40e73145a48060bff4783a116c34cc7241532d0d94198fb2c9629
sleep 5
docker ps
#----------------------------------------------
# check if container persists between runs
#----------------------------------------------
- name: Check server
run: |
docker ps
#----------------------------------------------
# check-out tests repo
#----------------------------------------------
- name: Checkout console test repo - name: Checkout console test repo
uses: actions/checkout@v3 uses: actions/checkout@v3
with: with:
repository: vegaprotocol/console-test repository: vegaprotocol/console-test
path: './console-test' path: './console-test'
#----------------------------------------------
- name: Set up Python # set-up python
#----------------------------------------------
- name: Set up python
id: setup-python
uses: actions/setup-python@v4 uses: actions/setup-python@v4
with: with:
python-version: '3.10.11' python-version: '3.10.11'
#----------------------------------------------
# ----- install & configure poetry -----
#----------------------------------------------
- name: Install Poetry - name: Install Poetry
uses: snok/install-poetry@v1 uses: snok/install-poetry@v1
with:
virtualenvs-create: true
virtualenvs-in-project: true
virtualenvs-path: console-test/.venv
#----------------------------------------------
# load cached venv if cache exists
#----------------------------------------------
- name: Load cached venv
id: cached-poetry-dependencies
uses: actions/cache@v3
with:
path: console-test/.venv
key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }}
#----------------------------------------------
# install dependencies if cache does not exist
#----------------------------------------------
- name: Install dependencies - name: Install dependencies
run: |
poetry install --no-root
working-directory: ./console-test working-directory: ./console-test
if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
- name: load Binaries run: poetry install --no-interaction --no-root
run: | #----------------------------------------------
poetry run python -m vega_sim.tools.load_binaries # install vega binaries
#----------------------------------------------
- name: Install vega binaries
working-directory: ./console-test working-directory: ./console-test
run: poetry run python -m vega_sim.tools.load_binaries --force
- name: pull console #----------------------------------------------
run: | # install playwright
poetry run docker pull ghcr.io/vegaprotocol/frontend/trading:${{ inputs.github-sha }} #----------------------------------------------
- name: Update container_name in config.py
run: |
sed -i "s/container_name = \".*\"/container_name = \"vegaprotocol\/frontend\/trading:${{ inputs.github-sha }}\"/g" config.py
- name: install playwright - name: install playwright
run: poetry run playwright install run: poetry run playwright install
working-directory: ./console-test working-directory: ./console-test
#----------------------------------------------
- name: run tests # run tests
run: poetry run pytest --numprocesses auto #----------------------------------------------
- name: Run tests
working-directory: ./console-test working-directory: ./console-test
run: poetry run pytest -v -s --numprocesses auto --dist loadfile --durations=20
- name: Check files
run: |
ls -al .
ls -al console-test
#----------------------------------------------
# upload traces
#----------------------------------------------
- name: Upload Playwright Trace - name: Upload Playwright Trace
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
if: always() if: always()
+1 -1
View File
@@ -20,7 +20,7 @@ jobs:
project: ${{ fromJSON(inputs.projects) }} project: ${{ fromJSON(inputs.projects) }}
name: ${{ matrix.project }} name: ${{ matrix.project }}
runs-on: self-hosted-runner runs-on: self-hosted-runner
timeout-minutes: 100 timeout-minutes: 120
steps: steps:
# Checks if skip cache was requested # Checks if skip cache was requested
- name: Set skip-nx-cache flag - name: Set skip-nx-cache flag
+20 -49
View File
@@ -29,6 +29,12 @@ jobs:
echo IS_TESTNET_RELEASE=false >> $GITHUB_ENV echo IS_TESTNET_RELEASE=false >> $GITHUB_ENV
echo IS_IPFS_RELEASE=false >> $GITHUB_ENV echo IS_IPFS_RELEASE=false >> $GITHUB_ENV
echo IS_S3_RELEASE=false >> $GITHUB_ENV echo IS_S3_RELEASE=false >> $GITHUB_ENV
echo IS_DEV_IMAGE=false >> $GITHUB_ENV
- name: Is dev image
if: ${{ contains(github.ref, 'develop') && github.event_name == 'push' && matrix.app == 'trading' }}
run: |
echo IS_DEV_IMAGE=true >> $GITHUB_ENV
- name: Is PR - name: Is PR
if: ${{ github.event_name == 'pull_request' }} if: ${{ github.event_name == 'pull_request' }}
@@ -36,7 +42,7 @@ jobs:
echo IS_PR=true >> $GITHUB_ENV echo IS_PR=true >> $GITHUB_ENV
- name: Is mainnet release - name: Is mainnet release
if: ${{ contains(github.ref, 'release/mainnnet') && !contains(github.ref, 'mirror') }} if: ${{ contains(github.ref, 'release/mainnet') && !contains(github.ref, 'mirror') }}
run: | run: |
echo IS_MAINNET_RELEASE=true >> $GITHUB_ENV echo IS_MAINNET_RELEASE=true >> $GITHUB_ENV
@@ -75,7 +81,7 @@ jobs:
- name: Log in to the Container registry (docker hub) - name: Log in to the Container registry (docker hub)
uses: docker/login-action@v2 uses: docker/login-action@v2
if: ${{ env.IS_IPFS_RELEASE == 'true' }} if: ${{ env.IS_IPFS_RELEASE == 'true' || env.IS_DEV_IMAGE == 'true' }}
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
@@ -97,52 +103,13 @@ jobs:
- name: Define dist variables - name: Define dist variables
if: ${{ github.event_name == 'push' }} if: ${{ github.event_name == 'push' }}
run: | run: |
envName='' python3 tools/ci/define-dist-variables.py --github-ref="${{ github.ref }}" --app="${{ matrix.app }}"
domain="vega.rocks"
bucketName=''
if [[ "${{ github.ref }}" =~ .*release/.* ]]; then - name: Verify script result
# remove prefixing release/ and take the first string limited by - which is supposed to be name of the environment for releasing (format: release/testnet-trading) if: ${{ github.event_name == 'push' }}
envName="$(echo ${{ github.ref }} | sed -e "s|refs/heads/release/||" | cut -d '-' -f 1 )" run: |
elif [[ "${{ github.ref }}" =~ .*develop$ ]]; then echo "BUCKET_NAME=${{ env.BUCKET_NAME }}"
envName="stagnet1" echo "ENV_NAME=${{ env.ENV_NAME }}"
if [[ "${{ matrix.app }}" = "multisig-signer" ]]; then
envName="mainnet"
bucketName="tools.vega.xyz"
fi
if [[ "${{ matrix.app }}" = "static" ]]; then
envName="mainnet"
bucketName="static.vega.xyz"
fi
if [[ "${{ matrix.app }}" = "ui-toolkit" ]]; then
envName="mainnet"
bucketName="ui.vega.rocks"
fi
elif [[ "${{ github.ref }}" =~ .*mainnet$ ]]; then
envName="mainnet"
fi
if [[ "${envName}" = "mainnet" ]]; then
domain="vega.xyz"
if [[ -z "${bucketName}" ]]; then
bucketName="${{ matrix.app }}.${domain}"
fi
elif [[ "${envName}" = "testnet" ]]; then
domain="fairground.wtf"
if [[ -z "${bucketName}" ]]; then
bucketName="${{ matrix.app }}.${domain}"
fi
fi
if [[ -z "${bucketName}" ]]; then
bucketName="${{ matrix.app }}.${envName}.${domain}"
fi
echo "bucket name: ${bucketName}"
echo "env name: ${envName}"
echo BUCKET_NAME=${bucketName} >> $GITHUB_ENV
echo ENV_NAME=${envName} >> $GITHUB_ENV
- name: Build local dist - name: Build local dist
run: | run: |
@@ -157,8 +124,12 @@ jobs:
elif [ "${{ matrix.app }}" = "ui-toolkit" ]; then elif [ "${{ matrix.app }}" = "ui-toolkit" ]; then
NODE_ENV=production yarn nx run ui-toolkit:build-storybook NODE_ENV=production yarn nx run ui-toolkit:build-storybook
DIST_LOCATION=dist/storybook/ui-toolkit DIST_LOCATION=dist/storybook/ui-toolkit
elif [ "${{ matrix.app }}" = "static" ]; then
yarn nx build static || (yarn install && yarn nx build static)
else else
$envCmd yarn nx build ${{ matrix.app }} || (yarn install && $envCmd yarn nx build ${{ matrix.app }}) $envCmd yarn nx build ${{ matrix.app }} || (yarn install && $envCmd yarn nx build ${{ matrix.app }})
fi
if [[ -z "$DIST_LOCATION" ]]; then
DIST_LOCATION=dist/apps/${{ matrix.app }} DIST_LOCATION=dist/apps/${{ matrix.app }}
fi fi
mv $DIST_LOCATION dist-result mv $DIST_LOCATION dist-result
@@ -208,7 +179,7 @@ jobs:
uses: docker/build-push-action@v3 uses: docker/build-push-action@v3
continue-on-error: true continue-on-error: true
id: dockerhub-push id: dockerhub-push
if: ${{ env.IS_IPFS_RELEASE == 'true' }} if: ${{ env.IS_IPFS_RELEASE == 'true' || env.IS_DEV_IMAGE == 'true' }}
with: with:
context: . context: .
file: docker/node-outside-docker.Dockerfile file: docker/node-outside-docker.Dockerfile
@@ -218,7 +189,7 @@ jobs:
ENV_NAME=${{ env.ENV_NAME }} ENV_NAME=${{ env.ENV_NAME }}
tags: | tags: |
vegaprotocol/${{ matrix.app }}:${{ github.sha }} vegaprotocol/${{ matrix.app }}:${{ github.sha }}
vegaprotocol/${{ matrix.app }}:${{ env.IS_MAINNET_RELEASE == 'true' && 'mainnet' || env.IS_TESTNET_RELEASE == 'true' && 'testnet' || '' }} vegaprotocol/${{ matrix.app }}:${{ env.IS_MAINNET_RELEASE == 'true' && 'mainnet' || env.IS_TESTNET_RELEASE == 'true' && 'testnet' || env.IS_DEV_IMAGE == 'true' && 'develop' || '' }}
- name: Publish dist as docker image (ghcr - retry) - name: Publish dist as docker image (ghcr - retry)
uses: docker/build-push-action@v3 uses: docker/build-push-action@v3
+1
View File
@@ -15,6 +15,7 @@ on:
- types - types
- utils - utils
- i18n - i18n
- wallet
jobs: jobs:
publish: publish:
+2 -2
View File
@@ -2,7 +2,7 @@
. "$(dirname "$0")/_/husky.sh" . "$(dirname "$0")/_/husky.sh"
# Lint commit messages to ensure they follow conventional commit standards # Lint commit messages to ensure they follow conventional commit standards
yarn commitlint --edit "${1}" # yarn commitlint --edit "${1}"
# Lint all staged files # Lint all staged files
yarn lint-staged # yarn lint-staged
+5
View File
@@ -0,0 +1,5 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
# Lint all staged files
# yarn lint-staged
+8
View File
@@ -0,0 +1,8 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
# Lint all staged files
# yarn nx format:check
# Test all projects with changes
# yarn nx affected -t test --exclude trading
@@ -40,7 +40,7 @@ context.skip('Transactions page', function () {
.first() .first()
.click({ force: true }); .click({ force: true });
} else { } else {
cy.log('Unable to find any transactions on page'); cy.slack('Unable to find any transactions on page');
cy.screenshot(); cy.screenshot();
} }
}); });
@@ -78,7 +78,7 @@ context.skip('Transactions page', function () {
} }
}); });
} else { } else {
cy.log('Unable to find any transactions on page'); cy.slack('Unable to find any transactions on page');
cy.screenshot(); cy.screenshot();
} }
}); });
+1 -1
View File
@@ -4,7 +4,7 @@ NX_TENDERMINT_WEBSOCKET_URL=wss://be.mainnet-mirror.vega.rocks/websocket
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996 NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/mainnet-mirror/vegawallet-mainnet-mirror.toml NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/mainnet-mirror/vegawallet-mainnet-mirror.toml
NX_VEGA_URL=https://api.mainnet-mirror.vega.rocks/graphql NX_VEGA_URL=https://api.mainnet-mirror.vega.rocks/graphql
NX_VEGA_ENV=MAINNET_MIRROR NX_VEGA_ENV=MAINNET-MIRROR
NX_BLOCK_EXPLORER=https://be.mainnet-mirror.vega.rocks/rest NX_BLOCK_EXPLORER=https://be.mainnet-mirror.vega.rocks/rest
NX_ETHERSCAN_URL=https://sepolia.etherscan.io NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_VEGA_GOVERNANCE_URL=https://governance.mainnet-mirror.vega.rocks NX_VEGA_GOVERNANCE_URL=https://governance.mainnet-mirror.vega.rocks
@@ -137,7 +137,7 @@ export const ProposalsTable = ({ data }: ProposalsTableProps) => {
hide: window.innerWidth <= BREAKPOINT_MD, hide: window.innerWidth <= BREAKPOINT_MD,
headerName: t('Enactment date'), headerName: t('Enactment date'),
field: 'terms.enactmentDatetime', field: 'terms.enactmentDatetime',
valueFormatter: ({ valueFormatte: ({
value, value,
}: VegaValueFormatterParams< }: VegaValueFormatterParams<
ProposalListFieldsFragment, ProposalListFieldsFragment,
@@ -1,5 +1,7 @@
import { Link, useParams } from 'react-router-dom'; import { Link, useParams } from 'react-router-dom';
import { DATA_SOURCES } from '../../../config';
import { getDateTimeFormat } from '@vegaprotocol/utils'; import { getDateTimeFormat } from '@vegaprotocol/utils';
import type { TendermintBlocksResponse } from '../tendermint-blocks-response';
import { RouteTitle } from '../../../components/route-title'; import { RouteTitle } from '../../../components/route-title';
import { TimeAgo } from '../../../components/time-ago'; import { TimeAgo } from '../../../components/time-ago';
import { import {
@@ -12,7 +14,7 @@ import { TxsPerBlock } from '../../../components/txs/txs-per-block';
import { AsyncRenderer, Button } from '@vegaprotocol/ui-toolkit'; import { AsyncRenderer, Button } from '@vegaprotocol/ui-toolkit';
import { Routes } from '../../route-names'; import { Routes } from '../../route-names';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import { useBlockInfo } from '@vegaprotocol/tendermint'; import { useFetch } from '@vegaprotocol/react-helpers';
import { NodeLink } from '../../../components/links'; import { NodeLink } from '../../../components/links';
import { useDocumentTitle } from '../../../hooks/use-document-title'; import { useDocumentTitle } from '../../../hooks/use-document-title';
import EmptyList from '../../../components/empty-list/empty-list'; import EmptyList from '../../../components/empty-list/empty-list';
@@ -22,7 +24,10 @@ const Block = () => {
useDocumentTitle(['Blocks', `Block #${block}`]); useDocumentTitle(['Blocks', `Block #${block}`]);
const { const {
state: { data: blockData, loading, error }, state: { data: blockData, loading, error },
} = useBlockInfo(Number(block)); } = useFetch<TendermintBlocksResponse>(
`${DATA_SOURCES.tendermintUrl}/block?height=${block}`,
{ cache: 'force-cache' }
);
return ( return (
<section> <section>
@@ -54,7 +54,7 @@ export const PartyBlockStake = ({
{p?.stakingSummary.currentStakeAvailable ? ( {p?.stakingSummary.currentStakeAvailable ? (
<KeyValueTable> <KeyValueTable>
<KeyValueTableRow noBorder={true}> <KeyValueTableRow noBorder={true}>
<div>{t('Associated to key')}</div> <div>{t('Available stake')}</div>
<div> <div>
<GovernanceAssetBalance <GovernanceAssetBalance
price={p.stakingSummary.currentStakeAvailable} price={p.stakingSummary.currentStakeAvailable}
@@ -62,7 +62,7 @@ export const PartyBlockStake = ({
</div> </div>
</KeyValueTableRow> </KeyValueTableRow>
<KeyValueTableRow noBorder={true}> <KeyValueTableRow noBorder={true}>
<div>{t('Staked to validator')}</div> <div>{t('Active stake')}</div>
<div> <div>
<GovernanceAssetBalance price={linkedStake || '0'} /> <GovernanceAssetBalance price={linkedStake || '0'} />
</div> </div>
@@ -67,7 +67,7 @@ const Party = () => {
text: t('Go back'), text: t('Go back'),
action: () => navigate(-1), action: () => navigate(-1),
className: 'py-1', className: 'py-1',
size: 'small', size: 'sm',
}} }}
/> />
</div> </div>
@@ -320,8 +320,8 @@ context(
// 3001-VOTE-076 // 3001-VOTE-076
cy.getByTestId(connectToVegaWalletButton) cy.getByTestId(connectToVegaWalletButton)
.should('be.visible') .should('be.visible')
.and('have.text', 'Connect Vega wallet'); .and('have.text', 'Connect Vega wallet')
cy.getByTestId(connectToVegaWalletButton).click(); .click();
cy.getByTestId('connector-jsonRpc').click(); cy.getByTestId('connector-jsonRpc').click();
cy.getByTestId(vegaWalletNameElement).should('be.visible'); cy.getByTestId(vegaWalletNameElement).should('be.visible');
cy.getByTestId(connectToVegaWalletButton).should('not.exist'); cy.getByTestId(connectToVegaWalletButton).should('not.exist');
@@ -161,7 +161,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
cy.getByTestId('menu-drawer').should('be.visible'); cy.getByTestId('menu-drawer').should('be.visible');
}); });
it('should have link for proposal page', function () { it.skip('should have link for proposal page', function () {
cy.getByTestId('menu-drawer').within(() => { cy.getByTestId('menu-drawer').within(() => {
cy.get('[href="/proposals"]') cy.get('[href="/proposals"]')
.should('exist') .should('exist')
@@ -1,12 +1,13 @@
/// <reference types="cypress" /> /// <reference types="cypress" />
import { import {
navigateTo,
navigation,
turnTelemetryOff, turnTelemetryOff,
waitForSpinner, waitForSpinner,
} from '../../support/common.functions'; } from '../../support/common.functions';
import { import {
createTenDigitUnixTimeStampForSpecifiedDays, enterUniqueFreeFormProposalBody,
enterRawProposalBody,
goToMakeNewProposal, goToMakeNewProposal,
governanceProposalType, governanceProposalType,
} from '../../support/governance.functions'; } from '../../support/governance.functions';
@@ -46,11 +47,12 @@ context('View functionality with public key', { tags: '@smoke' }, function () {
.and('contain.text', 'USDC (fake)'); .and('contain.text', 'USDC (fake)');
}); });
it('Unable to submit proposal with public key', function () { it.skip('Unable to submit proposal with public key', function () {
const expectedErrorTxt = `You are connected in a view only state for public key: ${vegaWalletPubKey}. In order to send transactions you must connect to a real wallet.`; const expectedErrorTxt = `You are connected in a view only state for public key: ${vegaWalletPubKey}. In order to send transactions you must connect to a real wallet.`;
goToMakeNewProposal(governanceProposalType.RAW); navigateTo(navigation.proposals);
enterRawProposalBody(createTenDigitUnixTimeStampForSpecifiedDays(8)); goToMakeNewProposal(governanceProposalType.FREEFORM);
enterUniqueFreeFormProposalBody('50', 'pub key proposal test');
cy.getByTestId('dialog-content') cy.getByTestId('dialog-content')
.first() .first()
.within(() => { .within(() => {
@@ -78,7 +78,7 @@ context(
cy.getByTestId('connector-jsonRpc') cy.getByTestId('connector-jsonRpc')
.should('be.visible') .should('be.visible')
.and('have.text', 'Connect Vega wallet'); .and('have.text', 'Connect Vega wallet');
cy.getByTestId('connector-rest') cy.getByTestId('connector-hosted')
.should('be.visible') .should('be.visible')
.and('have.text', 'Hosted Fairground wallet'); .and('have.text', 'Hosted Fairground wallet');
}); });
@@ -94,7 +94,7 @@ context(
describe('when rest connector form opened', function () { describe('when rest connector form opened', function () {
before('click hosted wallet app button', function () { before('click hosted wallet app button', function () {
cy.getByTestId(connectorsList).within(() => { cy.getByTestId(connectorsList).within(() => {
cy.getByTestId('connector-rest').click(); cy.getByTestId('connector-hosted').click();
}); });
}); });
-4
View File
@@ -18,10 +18,6 @@ NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/annou
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72 NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
NX_VEGA_REST_URL=https://api.n00.stagnet1.vega.xyz/api/v2/ NX_VEGA_REST_URL=https://api.n00.stagnet1.vega.xyz/api/v2/
NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.rocks
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
#Test configuration variables #Test configuration variables
CYPRESS_FAIRGROUND=false CYPRESS_FAIRGROUND=false
LC_ALL="en_US.UTF-8" LC_ALL="en_US.UTF-8"
-3
View File
@@ -19,9 +19,6 @@ NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
NX_VEGA_REST_URL=http://localhost:3008/api/v2/ NX_VEGA_REST_URL=http://localhost:3008/api/v2/
NX_TENDERMINT_URL=http://localhost:26617
NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket
#Test configuration variables #Test configuration variables
CYPRESS_FAIRGROUND=false CYPRESS_FAIRGROUND=false
-3
View File
@@ -13,6 +13,3 @@ NX_TRANCHES_SERVICE_URL=https://tranches-devnet1-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_REST_URL=https://api.n00.devnet1.vega.xyz/api/v2/ NX_VEGA_REST_URL=https://api.n00.devnet1.vega.xyz/api/v2/
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996 NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
NX_TENDERMINT_URL=https://tm.be.devnet1.vega.xyz/
NX_TENDERMINT_WEBSOCKET_URL=wss://be.devnet1.vega.xyz/websocket
-3
View File
@@ -14,6 +14,3 @@ NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-mainnet-k8s.ops.vega.xyz NX_TRANCHES_SERVICE_URL=https://tranches-mainnet-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
NX_VEGA_REST_URL=https://api.vega.community/api/v2/ NX_VEGA_REST_URL=https://api.vega.community/api/v2/
NX_TENDERMINT_URL=https://be.vega.community
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
+2 -5
View File
@@ -1,8 +1,8 @@
# App configuration variables # App configuration variables
NX_VEGA_ENV=MAINNET_MIRROR NX_VEGA_ENV=MAINNET-MIRROR
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/mainnet-mirror/vegawallet-mainnet-mirror.toml NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/mainnet-mirror/vegawallet-mainnet-mirror.toml
NX_VEGA_URL=https://api.mainnet-mirror.vega.rocks/graphql NX_VEGA_URL=https://api.mainnet-mirror.vega.rocks/graphql
NX_VEGA_NETWORKS='{"DEVNET":"https://dev.governance.vega.xyz","TESTNET":"https://governance.fairground.wtf","MAINNET":"https://governance.vega.xyz","MAINNET_MIRROR":"https://governance.mainnet-mirror.vega.rocks","STAGNET1":"https://trading.stagnet1.vega.rocks"}' NX_VEGA_NETWORKS='{"DEVNET":"https://dev.governance.vega.xyz","TESTNET":"https://governance.fairground.wtf","MAINNET":"https://governance.vega.xyz","MAINNET-MIRROR":"https://governance.mainnet-mirror.vega.rocks","STAGNET1":"https://trading.stagnet1.vega.rocks"}'
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8 NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://sepolia.etherscan.io NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
@@ -13,6 +13,3 @@ NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-mainnet-mirror-k8s.ops.vega.xyz NX_TRANCHES_SERVICE_URL=https://tranches-mainnet-mirror-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
NX_VEGA_REST_URL=https://api.mainnet-mirror.vega.rocks/api/v2/ NX_VEGA_REST_URL=https://api.mainnet-mirror.vega.rocks/api/v2/
NX_TENDERMINT_URL=https://be.mainnet-mirror.vega.rocks
NX_TENDERMINT_WEBSOCKET_URL=wss://be.mainnet-mirror.vega.rocks/websocket
-3
View File
@@ -9,6 +9,3 @@ NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_REST_URL=https://api.n00.stagnet1.vega.xyz/api/v2/ NX_VEGA_REST_URL=https://api.n00.stagnet1.vega.xyz/api/v2/
NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.rocks
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
-3
View File
@@ -14,6 +14,3 @@ NX_TRANCHES_SERVICE_URL=https://tranches-testnet-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_REST_URL=https://api.n07.testnet.vega.xyz/api/v2/ NX_VEGA_REST_URL=https://api.n07.testnet.vega.xyz/api/v2/
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996 NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
-3
View File
@@ -11,6 +11,3 @@ NX_VEGA_EXPLORER_URL=https://explorer.validators-testnet.vega.rocks/
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_REST_URL=https://api-validators-testnet.vega.rocks/api/v2/ NX_VEGA_REST_URL=https://api-validators-testnet.vega.rocks/api/v2/
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996 NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
NX_TENDERMINT_URL=https://tm.be.validators-testnet.vega.rocks
NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.
@@ -2,8 +2,13 @@ import { Button } from '@vegaprotocol/ui-toolkit';
import React from 'react'; import React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useVegaWalletDialogStore } from '@vegaprotocol/wallet'; import { useVegaWalletDialogStore } from '@vegaprotocol/wallet';
import {
AppStateActionType,
useAppState,
} from '../../contexts/app-state/app-state-context';
export const ConnectToVega = () => { export const ConnectToVega = () => {
const { appDispatch } = useAppState();
const { t } = useTranslation(); const { t } = useTranslation();
const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({ const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
openVegaWalletDialog: store.openVegaWalletDialog, openVegaWalletDialog: store.openVegaWalletDialog,
@@ -11,6 +16,10 @@ export const ConnectToVega = () => {
return ( return (
<Button <Button
onClick={() => { onClick={() => {
appDispatch({
type: AppStateActionType.SET_VEGA_WALLET_OVERLAY,
isOpen: true,
});
openVegaWalletDialog(); openVegaWalletDialog();
}} }}
data-testid="connect-to-vega-wallet-btn" data-testid="connect-to-vega-wallet-btn"
+1 -12
View File
@@ -18,10 +18,6 @@ import { VegaWallet } from '../vega-wallet';
import { useLocation, useMatch } from 'react-router-dom'; import { useLocation, useMatch } from 'react-router-dom';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { useTelemetryDialog } from '../telemetry-dialog/telemetry-dialog'; import { useTelemetryDialog } from '../telemetry-dialog/telemetry-dialog';
import {
ProtocolUpgradeCountdown,
ProtocolUpgradeCountdownMode,
} from '@vegaprotocol/proposals';
export const SettingsLink = () => { export const SettingsLink = () => {
const { open, isOpen, close } = useTelemetryDialog(); const { open, isOpen, close } = useTelemetryDialog();
@@ -65,14 +61,7 @@ export const Nav = ({ theme }: Pick<NavigationProps, 'theme'>) => {
appName="Governance" appName="Governance"
theme={theme} theme={theme}
breakpoints={[458, 959]} breakpoints={[458, 959]}
actions={ actions={<SettingsLink />}
<>
<SettingsLink />
<ProtocolUpgradeCountdown
mode={ProtocolUpgradeCountdownMode.IN_ESTIMATED_TIME_REMAINING}
/>
</>
}
> >
<NavigationList <NavigationList
className="[.drawer-content_&]:border-b [.drawer-content_&]:border-b-vega-light-200 dark:[.drawer-content_&]:border-b-vega-dark-200 [.drawer-content_&]:pb-8 [.drawer-content_&]:mb-2" className="[.drawer-content_&]:border-b [.drawer-content_&]:border-b-vega-light-200 dark:[.drawer-content_&]:border-b-vega-dark-200 [.drawer-content_&]:pb-8 [.drawer-content_&]:mb-2"
@@ -4,11 +4,7 @@ import type { ReactNode } from 'react';
import { AnnouncementBanner } from '@vegaprotocol/announcements'; import { AnnouncementBanner } from '@vegaprotocol/announcements';
import { Nav } from '../nav'; import { Nav } from '../nav';
import { Networks, useEnvironment } from '@vegaprotocol/environment'; import { Networks, useEnvironment } from '@vegaprotocol/environment';
import { import React from 'react';
ProtocolUpgradeCountdownMode,
ProtocolUpgradeProposalNotification,
} from '@vegaprotocol/proposals';
import { ViewingAsBanner } from '@vegaprotocol/ui-toolkit';
interface AppLayoutProps { interface AppLayoutProps {
children: ReactNode; children: ReactNode;
@@ -35,23 +31,8 @@ export const AppLayout = ({ children }: AppLayoutProps) => {
/> />
)} )}
<Nav theme={VEGA_ENV === Networks.TESTNET ? 'yellow' : 'dark'} /> <Nav theme={VEGA_ENV === Networks.TESTNET ? 'yellow' : 'dark'} />
<NotificationsContainer />
</div> </div>
<div className={AppLayoutClasses}>{children}</div> <div className={AppLayoutClasses}>{children}</div>
</div> </div>
); );
}; };
const NotificationsContainer = () => {
const { isReadOnly, pubKey, disconnect } = useVegaWallet();
return (
<div data-testid="banners">
<ProtocolUpgradeProposalNotification
mode={ProtocolUpgradeCountdownMode.IN_ESTIMATED_TIME_REMAINING}
/>
{isReadOnly ? (
<ViewingAsBanner pubKey={pubKey} disconnect={disconnect} />
) : null}
</div>
);
};
@@ -1,3 +1,5 @@
import { ViewingAsBanner } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet';
import React from 'react'; import React from 'react';
export interface TemplateSidebarProps { export interface TemplateSidebarProps {
@@ -6,18 +8,24 @@ export interface TemplateSidebarProps {
} }
export function TemplateSidebar({ children, sidebar }: TemplateSidebarProps) { export function TemplateSidebar({ children, sidebar }: TemplateSidebarProps) {
const { isReadOnly, pubKey, disconnect } = useVegaWallet();
return ( return (
<div className="w-full border-b border-neutral-700 lg:grid lg:grid-rows-[1fr] lg:grid-cols-[1fr_450px]"> <>
<main className="max-w-[100vw] col-start-1 p-4 overflow-auto"> {isReadOnly ? (
{children} <ViewingAsBanner pubKey={pubKey} disconnect={disconnect} />
</main> ) : null}
<aside className="col-start-2 row-start-1 row-span-2 hidden lg:block p-4 bg-banner bg-contain border-l border-neutral-700"> <div className="w-full border-b border-neutral-700 lg:grid lg:grid-rows-[1fr] lg:grid-cols-[1fr_450px]">
{sidebar.map((Component, i) => ( <main className="max-w-[100vw] col-start-1 p-4 overflow-auto">
<section className="mb-4 last:mb-0" key={i}> {children}
{Component} </main>
</section> <aside className="col-start-2 row-start-1 row-span-2 hidden lg:block p-4 bg-banner bg-contain border-l border-neutral-700">
))} {sidebar.map((Component, i) => (
</aside> <section className="mb-4 last:mb-0" key={i}>
</div> {Component}
</section>
))}
</aside>
</div>
</>
); );
} }
@@ -3,6 +3,11 @@ import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
import React from 'react'; import React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import {
AppStateActionType,
useAppState,
} from '../../contexts/app-state/app-state-context';
interface VegaWalletContainerProps { interface VegaWalletContainerProps {
children: (key: string) => React.ReactElement; children: (key: string) => React.ReactElement;
} }
@@ -10,6 +15,7 @@ interface VegaWalletContainerProps {
export const VegaWalletContainer = ({ children }: VegaWalletContainerProps) => { export const VegaWalletContainer = ({ children }: VegaWalletContainerProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { pubKey } = useVegaWallet(); const { pubKey } = useVegaWallet();
const { appDispatch } = useAppState();
const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({ const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
openVegaWalletDialog: store.openVegaWalletDialog, openVegaWalletDialog: store.openVegaWalletDialog,
})); }));
@@ -19,6 +25,10 @@ export const VegaWalletContainer = ({ children }: VegaWalletContainerProps) => {
<Button <Button
data-testid="connect-to-vega-wallet-btn" data-testid="connect-to-vega-wallet-btn"
onClick={() => { onClick={() => {
appDispatch({
type: AppStateActionType.SET_VEGA_WALLET_OVERLAY,
isOpen: true,
});
openVegaWalletDialog(); openVegaWalletDialog();
}} }}
> >
@@ -71,6 +71,7 @@ export const VegaWallet = () => {
const VegaWalletNotConnected = () => { const VegaWalletNotConnected = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { appDispatch } = useAppState();
const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({ const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
openVegaWalletDialog: store.openVegaWalletDialog, openVegaWalletDialog: store.openVegaWalletDialog,
})); }));
@@ -78,6 +79,10 @@ const VegaWalletNotConnected = () => {
<> <>
<Button <Button
onClick={() => { onClick={() => {
appDispatch({
type: AppStateActionType.SET_VEGA_WALLET_OVERLAY,
isOpen: true,
});
openVegaWalletDialog(); openVegaWalletDialog();
}} }}
fill={true} fill={true}
-4
View File
@@ -25,10 +25,6 @@ export const ContractAddresses: {
claimAddress: customClaimAddress ?? '0x0', claimAddress: customClaimAddress ?? '0x0',
lockedAddress: customLockedAddress ?? '0x0', lockedAddress: customLockedAddress ?? '0x0',
}, },
MAINNET_MIRROR: {
claimAddress: '0x8Cef746ab7C83B61F6461cC92882bD61AB65a994', // TODO not deployed to this env, but random address so app doesn't error
lockedAddress: '0x0', // TODO not deployed to this env
},
DEVNET: { DEVNET: {
claimAddress: '0x8Cef746ab7C83B61F6461cC92882bD61AB65a994', // TODO not deployed to this env, but random address so app doesn't error claimAddress: '0x8Cef746ab7C83B61F6461cC92882bD61AB65a994', // TODO not deployed to this env, but random address so app doesn't error
lockedAddress: '0x0', // TODO not deployed to this env lockedAddress: '0x0', // TODO not deployed to this env
@@ -28,6 +28,9 @@ export interface AppState {
/** Total number of VEGA Tokens, both vesting and unlocked, associated for staking */ /** Total number of VEGA Tokens, both vesting and unlocked, associated for staking */
totalAssociated: BigNumber; totalAssociated: BigNumber;
/** Whether or not the connect to VEGA wallet overlay is open */
vegaWalletOverlay: boolean;
/** Whether or not the manage VEGA wallet overlay is open */ /** Whether or not the manage VEGA wallet overlay is open */
vegaWalletManageOverlay: boolean; vegaWalletManageOverlay: boolean;
@@ -49,7 +52,9 @@ export enum AppStateActionType {
SET_TOKEN, SET_TOKEN,
SET_ALLOWANCE, SET_ALLOWANCE,
REFRESH_BALANCES, REFRESH_BALANCES,
SET_VEGA_WALLET_OVERLAY,
SET_VEGA_WALLET_MANAGE_OVERLAY, SET_VEGA_WALLET_MANAGE_OVERLAY,
SET_DRAWER,
REFRESH_ASSOCIATED_BALANCES, REFRESH_ASSOCIATED_BALANCES,
SET_ASSOCIATION_BREAKDOWN, SET_ASSOCIATION_BREAKDOWN,
SET_TRANSACTION_OVERLAY, SET_TRANSACTION_OVERLAY,
@@ -64,10 +69,18 @@ export type AppStateAction =
totalSupply: BigNumber; totalSupply: BigNumber;
totalAssociated: BigNumber; totalAssociated: BigNumber;
} }
| {
type: AppStateActionType.SET_VEGA_WALLET_OVERLAY;
isOpen: boolean;
}
| { | {
type: AppStateActionType.SET_VEGA_WALLET_MANAGE_OVERLAY; type: AppStateActionType.SET_VEGA_WALLET_MANAGE_OVERLAY;
isOpen: boolean; isOpen: boolean;
} }
| {
type: AppStateActionType.SET_DRAWER;
isOpen: boolean;
}
| { | {
type: AppStateActionType.SET_TRANSACTION_OVERLAY; type: AppStateActionType.SET_TRANSACTION_OVERLAY;
isOpen: boolean; isOpen: boolean;
@@ -14,6 +14,7 @@ const initialAppState: AppState = {
totalAssociated: new BigNumber(0), totalAssociated: new BigNumber(0),
decimals: 0, decimals: 0,
totalSupply: new BigNumber(0), totalSupply: new BigNumber(0),
vegaWalletOverlay: false,
vegaWalletManageOverlay: false, vegaWalletManageOverlay: false,
transactionOverlay: false, transactionOverlay: false,
bannerMessage: '', bannerMessage: '',
@@ -30,10 +31,23 @@ function appStateReducer(state: AppState, action: AppStateAction): AppState {
totalAssociated: action.totalAssociated, totalAssociated: action.totalAssociated,
}; };
} }
case AppStateActionType.SET_VEGA_WALLET_OVERLAY: {
return {
...state,
vegaWalletOverlay: action.isOpen,
};
}
case AppStateActionType.SET_VEGA_WALLET_MANAGE_OVERLAY: { case AppStateActionType.SET_VEGA_WALLET_MANAGE_OVERLAY: {
return { return {
...state, ...state,
vegaWalletManageOverlay: action.isOpen, vegaWalletManageOverlay: action.isOpen,
vegaWalletOverlay: action.isOpen ? false : state.vegaWalletOverlay,
};
}
case AppStateActionType.SET_DRAWER: {
return {
...state,
vegaWalletOverlay: false,
}; };
} }
case AppStateActionType.SET_TRANSACTION_OVERLAY: { case AppStateActionType.SET_TRANSACTION_OVERLAY: {
@@ -837,7 +837,5 @@
"RejectedProposals": "Rejected proposals", "RejectedProposals": "Rejected proposals",
"networkGovernance": "Network governance", "networkGovernance": "Network governance",
"networkUpgrades": "Network upgrades", "networkUpgrades": "Network upgrades",
"assetSpecification": "Asset specification", "assetSpecification": "Asset specification"
"Estimated time to upgrade": "Estimated time to upgrade",
"Upgraded at": "Upgraded at"
} }
+1 -1
View File
@@ -7,9 +7,9 @@ import {
const urlParams = new URLSearchParams(window.location.search); const urlParams = new URLSearchParams(window.location.search);
export const injected = new InjectedConnector();
export const rest = new RestConnector(); export const rest = new RestConnector();
export const jsonRpc = new JsonRpcConnector(); export const jsonRpc = new JsonRpcConnector();
export const injected = new InjectedConnector();
export const view = new ViewConnector(urlParams.get('address')); export const view = new ViewConnector(urlParams.get('address'));
export const Connectors = { export const Connectors = {
@@ -31,7 +31,6 @@ import {
orderByDate, orderByDate,
orderByUpgradeBlockHeight, orderByUpgradeBlockHeight,
} from '../proposals/components/proposals-list/proposals-list'; } from '../proposals/components/proposals-list/proposals-list';
import { BigNumber } from '../../lib/bignumber';
const nodesToShow = 6; const nodesToShow = 6;
@@ -249,8 +248,6 @@ const GovernanceHome = ({ name }: RouteChildProps) => {
const activeNodes = removePaginationWrapper( const activeNodes = removePaginationWrapper(
validatorsData?.nodesConnection.edges validatorsData?.nodesConnection.edges
).filter((node) =>
new BigNumber(node.rankingScore.performanceScore).isGreaterThan(0)
); );
const trimmedActiveNodes = activeNodes?.slice(0, nodesToShow); const trimmedActiveNodes = activeNodes?.slice(0, nodesToShow);
@@ -4,20 +4,16 @@ import { render } from '@testing-library/react';
import { ProtocolUpgradeProposalStatus } from '@vegaprotocol/types'; import { ProtocolUpgradeProposalStatus } from '@vegaprotocol/types';
import { ProtocolUpgradeProposalDetailInfo } from './protocol-upgrade-proposal-detail-info'; import { ProtocolUpgradeProposalDetailInfo } from './protocol-upgrade-proposal-detail-info';
const PROPOSAL = { const renderComponent = () =>
vegaReleaseTag: 'v0.1.234',
status:
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_PENDING,
upgradeBlockHeight: '12345',
approvers: [],
};
const renderComponent = (lastBlockHeight?: string, time?: string) =>
render( render(
<ProtocolUpgradeProposalDetailInfo <ProtocolUpgradeProposalDetailInfo
proposal={PROPOSAL} proposal={{
lastBlockHeight={lastBlockHeight} vegaReleaseTag: 'v0.1.234',
time={time} status:
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_PENDING,
upgradeBlockHeight: '12345',
approvers: [],
}}
/> />
); );
@@ -47,28 +43,4 @@ describe('ProtocolUpgradeProposalDetailInfo', () => {
'v0.1.234' 'v0.1.234'
); );
}); });
it('should display estimated time if proposal is still pending', () => {
const { getByTestId } = renderComponent('12', 'time');
expect(getByTestId('protocol-upgrade-time-label')).toHaveTextContent(
'Estimated time to upgrade'
);
expect(getByTestId('protocol-upgrade-time')).toHaveTextContent('time');
});
it('should display upgraded at if proposal is done', () => {
const { getByTestId } = renderComponent('123456', 'time');
expect(getByTestId('protocol-upgrade-time-label')).toHaveTextContent(
'Upgraded at'
);
expect(getByTestId('protocol-upgrade-time')).toHaveTextContent('time');
});
it('should not display time if none provided', () => {
const { queryByTestId } = renderComponent('123456', undefined);
expect(
queryByTestId('protocol-upgrade-time-label')
).not.toBeInTheDocument();
expect(queryByTestId('protocol-upgrade-time')).not.toBeInTheDocument();
});
}); });
@@ -5,33 +5,19 @@ import {
RoundedWrapper, RoundedWrapper,
} from '@vegaprotocol/ui-toolkit'; } from '@vegaprotocol/ui-toolkit';
import { SubHeading } from '../../../../components/heading'; import { SubHeading } from '../../../../components/heading';
import { type ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals'; import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
import * as Types from '@vegaprotocol/types';
import type { ReactNode } from 'react';
export interface ProtocolUpgradeProposalDetailInfoProps { export interface ProtocolUpgradeProposalDetailInfoProps {
proposal: ProtocolUpgradeProposalFieldsFragment; proposal: ProtocolUpgradeProposalFieldsFragment;
lastBlockHeight?: string; lastBlockHeight?: string;
time?: string | ReactNode;
} }
export const ProtocolUpgradeProposalDetailInfo = ({ export const ProtocolUpgradeProposalDetailInfo = ({
proposal, proposal,
lastBlockHeight, lastBlockHeight,
time,
}: ProtocolUpgradeProposalDetailInfoProps) => { }: ProtocolUpgradeProposalDetailInfoProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const shouldShowUpgradeTime =
![
Types.ProtocolUpgradeProposalStatus
.PROTOCOL_UPGRADE_PROPOSAL_STATUS_REJECTED,
Types.ProtocolUpgradeProposalStatus
.PROTOCOL_UPGRADE_PROPOSAL_STATUS_UNSPECIFIED,
].includes(proposal.status) && time;
const pending = Number(proposal.upgradeBlockHeight) > Number(lastBlockHeight);
return ( return (
<div className="mb-10"> <div className="mb-10">
<SubHeading title={t('proposal')} /> <SubHeading title={t('proposal')} />
@@ -43,26 +29,13 @@ export const ProtocolUpgradeProposalDetailInfo = ({
<span data-testid="protocol-upgrade-block-height"> <span data-testid="protocol-upgrade-block-height">
{proposal.upgradeBlockHeight}{' '} {proposal.upgradeBlockHeight}{' '}
{lastBlockHeight && ( {lastBlockHeight && (
<span className="text-vega-light-300 dark:text-vega-dark-300"> <>
({t('currently')} {lastBlockHeight}) ({t('currently')} {lastBlockHeight})
</span> </>
)} )}
</span> </span>
</KeyValueTableRow> </KeyValueTableRow>
{shouldShowUpgradeTime && (
<KeyValueTableRow>
<span
data-testid="protocol-upgrade-time-label"
className="uppercase"
>
{pending ? t('Estimated time to upgrade') : t('Upgraded at')}
</span>
<span data-testid="protocol-upgrade-time">{time}</span>
</KeyValueTableRow>
)}
<KeyValueTableRow> <KeyValueTableRow>
<span className="uppercase">{t('state')}</span> <span className="uppercase">{t('state')}</span>
@@ -10,7 +10,10 @@ import {
} from '@vegaprotocol/ui-toolkit'; } from '@vegaprotocol/ui-toolkit';
import { addDecimal, toBigNum } from '@vegaprotocol/utils'; import { addDecimal, toBigNum } from '@vegaprotocol/utils';
import { ProposalState, VoteValue } from '@vegaprotocol/types'; import { ProposalState, VoteValue } from '@vegaprotocol/types';
import { useAppState } from '../../../../contexts/app-state/app-state-context'; import {
AppStateActionType,
useAppState,
} from '../../../../contexts/app-state/app-state-context';
import { BigNumber } from '../../../../lib/bignumber'; import { BigNumber } from '../../../../lib/bignumber';
import { DATE_FORMAT_LONG } from '../../../../lib/date-formats'; import { DATE_FORMAT_LONG } from '../../../../lib/date-formats';
import { VoteState } from './use-user-vote'; import { VoteState } from './use-user-vote';
@@ -70,6 +73,7 @@ export const VoteButtons = ({
dialog: Dialog, dialog: Dialog,
}: VoteButtonsProps) => { }: VoteButtonsProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { appDispatch } = useAppState();
const { pubKey } = useVegaWallet(); const { pubKey } = useVegaWallet();
const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({ const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
openVegaWalletDialog: store.openVegaWalletDialog, openVegaWalletDialog: store.openVegaWalletDialog,
@@ -94,6 +98,10 @@ export const VoteButtons = ({
<div data-testid="connect-wallet"> <div data-testid="connect-wallet">
<ButtonLink <ButtonLink
onClick={() => { onClick={() => {
appDispatch({
type: AppStateActionType.SET_VEGA_WALLET_OVERLAY,
isOpen: true,
});
openVegaWalletDialog(); openVegaWalletDialog();
}} }}
> >
@@ -134,6 +142,7 @@ export const VoteButtons = ({
minVoterBalance, minVoterBalance,
spamProtectionMinTokens, spamProtectionMinTokens,
t, t,
appDispatch,
openVegaWalletDialog, openVegaWalletDialog,
]); ]);
@@ -14,6 +14,7 @@ const mockAppState: AppState = {
totalAssociated: new BigNumber('50063005'), totalAssociated: new BigNumber('50063005'),
decimals: 18, decimals: 18,
totalSupply: mockTotalSupply, totalSupply: mockTotalSupply,
vegaWalletOverlay: false,
vegaWalletManageOverlay: false, vegaWalletManageOverlay: false,
transactionOverlay: false, transactionOverlay: false,
bannerMessage: '', bannerMessage: '',
File diff suppressed because one or more lines are too long
@@ -3,12 +3,12 @@ import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client'; import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client'; import * as Apollo from '@apollo/client';
const defaultOptions = {} as const; const defaultOptions = {} as const;
export type ProposalFieldsFragment = { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: string }, terms: { __typename?: 'ProposalTerms', closingDatetime: any, enactmentDatetime?: any | null, change: { __typename?: 'CancelTransfer' } | { __typename: 'NewAsset', name: string, symbol: string, decimals: number, quantum: string, source: { __typename?: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename?: 'ERC20', contractAddress: string, withdrawThreshold: string, lifetimeLimit: string } } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, futureProduct?: { __typename?: 'FutureProduct', settlementAsset: { __typename?: 'Asset', symbol: string } } | null } } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset', quantum: string, assetId: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename?: 'UpdateMarket', marketId: string } | { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string } } }; export type ProposalFieldsFragment = { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: string }, terms: { __typename?: 'ProposalTerms', closingDatetime: any, enactmentDatetime?: any | null, change: { __typename: 'NewAsset', name: string, symbol: string, decimals: number, quantum: string, source: { __typename?: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename?: 'ERC20', contractAddress: string, withdrawThreshold: string, lifetimeLimit: string } } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, futureProduct?: { __typename?: 'FutureProduct', settlementAsset: { __typename?: 'Asset', symbol: string } } | null } } | { __typename?: 'UpdateAsset', quantum: string, assetId: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename?: 'UpdateMarket', marketId: string } | { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string } } };
export type ProposalsQueryVariables = Types.Exact<{ [key: string]: never; }>; export type ProposalsQueryVariables = Types.Exact<{ [key: string]: never; }>;
export type ProposalsQuery = { __typename?: 'Query', proposalsConnection?: { __typename?: 'ProposalsConnection', edges?: Array<{ __typename?: 'ProposalEdge', node: { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: string }, terms: { __typename?: 'ProposalTerms', closingDatetime: any, enactmentDatetime?: any | null, change: { __typename?: 'CancelTransfer' } | { __typename: 'NewAsset', name: string, symbol: string, decimals: number, quantum: string, source: { __typename?: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename?: 'ERC20', contractAddress: string, withdrawThreshold: string, lifetimeLimit: string } } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, futureProduct?: { __typename?: 'FutureProduct', settlementAsset: { __typename?: 'Asset', symbol: string } } | null } } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset', quantum: string, assetId: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename?: 'UpdateMarket', marketId: string } | { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string } } } } | null> | null } | null }; export type ProposalsQuery = { __typename?: 'Query', proposalsConnection?: { __typename?: 'ProposalsConnection', edges?: Array<{ __typename?: 'ProposalEdge', node: { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: string }, terms: { __typename?: 'ProposalTerms', closingDatetime: any, enactmentDatetime?: any | null, change: { __typename: 'NewAsset', name: string, symbol: string, decimals: number, quantum: string, source: { __typename?: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename?: 'ERC20', contractAddress: string, withdrawThreshold: string, lifetimeLimit: string } } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, futureProduct?: { __typename?: 'FutureProduct', settlementAsset: { __typename?: 'Asset', symbol: string } } | null } } | { __typename?: 'UpdateAsset', quantum: string, assetId: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename?: 'UpdateMarket', marketId: string } | { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string } } } } | null> | null } | null };
export const ProposalFieldsFragmentDoc = gql` export const ProposalFieldsFragmentDoc = gql`
fragment ProposalFields on Proposal { fragment ProposalFields on Proposal {
@@ -1,23 +1,14 @@
import { useEffect, useMemo } from 'react'; import { useEffect, useMemo } from 'react';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit'; import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import { import { removePaginationWrapper, stripFullStops } from '@vegaprotocol/utils';
convertToCountdownString,
formatDateWithLocalTimezone,
removePaginationWrapper,
stripFullStops,
} from '@vegaprotocol/utils';
import * as Schema from '@vegaprotocol/types'; import * as Schema from '@vegaprotocol/types';
import { ProtocolUpgradeProposal } from './protocol-upgrade-proposal'; import { ProtocolUpgradeProposal } from './protocol-upgrade-proposal';
import { ProposalNotFound } from '../components/proposal-not-found'; import { ProposalNotFound } from '../components/proposal-not-found';
import { useNodesQuery } from '../../staking/home/__generated__/Nodes'; import { useNodesQuery } from '../../staking/home/__generated__/Nodes';
import { useRefreshAfterEpoch } from '../../../hooks/use-refresh-after-epoch'; import { useRefreshAfterEpoch } from '../../../hooks/use-refresh-after-epoch';
import { import { useProtocolUpgradeProposalsQuery } from '@vegaprotocol/proposals';
useProtocolUpgradeProposalsQuery,
useTimeToUpgrade,
} from '@vegaprotocol/proposals';
import { useBlockInfo } from '@vegaprotocol/tendermint';
export const ProtocolUpgradeProposalContainer = () => { export const ProtocolUpgradeProposalContainer = () => {
const params = useParams<{ proposalReleaseTag: string }>(); const params = useParams<{ proposalReleaseTag: string }>();
@@ -67,20 +58,6 @@ export const ProtocolUpgradeProposalContainer = () => {
); );
}, [nodesData]); }, [nodesData]);
const pending =
Number(protocolUpgradeProposal?.upgradeBlockHeight) >
Number(data?.lastBlockHeight);
const {
state: { data: blockInfo },
} = useBlockInfo(
Number(protocolUpgradeProposal?.upgradeBlockHeight),
!pending
);
const time = useTimeToUpgrade(
Number(protocolUpgradeProposal?.upgradeBlockHeight)
);
return ( return (
<AsyncRenderer <AsyncRenderer
loading={loading || nodesLoading} loading={loading || nodesLoading}
@@ -92,19 +69,6 @@ export const ProtocolUpgradeProposalContainer = () => {
proposal={protocolUpgradeProposal} proposal={protocolUpgradeProposal}
lastBlockHeight={data?.lastBlockHeight} lastBlockHeight={data?.lastBlockHeight}
consensusValidators={consensusValidators} consensusValidators={consensusValidators}
time={
pending && time ? (
convertToCountdownString(time, '0:00:00:00')
) : blockInfo?.result ? (
<span title={blockInfo.result.block.header.time}>
{formatDateWithLocalTimezone(
new Date(blockInfo.result.block.header.time)
)}
</span>
) : (
'-'
)
}
/> />
) : ( ) : (
<ProposalNotFound /> <ProposalNotFound />
@@ -1,11 +1,10 @@
import { useMemo } from 'react'; import { useMemo } from 'react';
import type { ReactNode } from 'react';
import { ProtocolUpgradeProposalDetailHeader } from '../components/protocol-upgrade-proposal-detail-header'; import { ProtocolUpgradeProposalDetailHeader } from '../components/protocol-upgrade-proposal-detail-header';
import { ProtocolUpdateProposalDetailApprovals } from '../components/protocol-upgrade-proposal-detail-approvals'; import { ProtocolUpdateProposalDetailApprovals } from '../components/protocol-upgrade-proposal-detail-approvals';
import { ProtocolUpgradeProposalDetailInfo } from '../components/protocol-upgrade-proposal-detail-info'; import { ProtocolUpgradeProposalDetailInfo } from '../components/protocol-upgrade-proposal-detail-info';
import { getNormalisedVotingPower } from '../../staking/shared'; import { getNormalisedVotingPower } from '../../staking/shared';
import type { NodesFragmentFragment } from '../../staking/home/__generated__/Nodes'; import type { NodesFragmentFragment } from '../../staking/home/__generated__/Nodes';
import { type ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals'; import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
import { useVegaRelease } from '@vegaprotocol/environment'; import { useVegaRelease } from '@vegaprotocol/environment';
import { ExternalLink } from '@vegaprotocol/ui-toolkit'; import { ExternalLink } from '@vegaprotocol/ui-toolkit';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@@ -14,7 +13,6 @@ export interface ProtocolUpgradeProposalProps {
proposal: ProtocolUpgradeProposalFieldsFragment; proposal: ProtocolUpgradeProposalFieldsFragment;
consensusValidators: NodesFragmentFragment[] | null; consensusValidators: NodesFragmentFragment[] | null;
lastBlockHeight?: string; lastBlockHeight?: string;
time?: string | ReactNode;
} }
export const getConsensusApprovals = ( export const getConsensusApprovals = (
@@ -46,7 +44,6 @@ export const ProtocolUpgradeProposal = ({
proposal, proposal,
lastBlockHeight, lastBlockHeight,
consensusValidators, consensusValidators,
time,
}: ProtocolUpgradeProposalProps) => { }: ProtocolUpgradeProposalProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const releaseInfo = useVegaRelease(proposal.vegaReleaseTag); const releaseInfo = useVegaRelease(proposal.vegaReleaseTag);
@@ -76,7 +73,6 @@ export const ProtocolUpgradeProposal = ({
<ProtocolUpgradeProposalDetailInfo <ProtocolUpgradeProposalDetailInfo
proposal={proposal} proposal={proposal}
lastBlockHeight={lastBlockHeight} lastBlockHeight={lastBlockHeight}
time={time}
/> />
{consensusValidators && consensusApprovals && ( {consensusValidators && consensusApprovals && (
@@ -2,9 +2,14 @@ import classNames from 'classnames';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useVegaWalletDialogStore } from '@vegaprotocol/wallet'; import { useVegaWalletDialogStore } from '@vegaprotocol/wallet';
import { Button } from '@vegaprotocol/ui-toolkit'; import { Button } from '@vegaprotocol/ui-toolkit';
import {
AppStateActionType,
useAppState,
} from '../../contexts/app-state/app-state-context';
import { SubHeading } from '../../components/heading'; import { SubHeading } from '../../components/heading';
export const ConnectToSeeRewards = () => { export const ConnectToSeeRewards = () => {
const { appDispatch } = useAppState();
const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({ const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
openVegaWalletDialog: store.openVegaWalletDialog, openVegaWalletDialog: store.openVegaWalletDialog,
})); }));
@@ -21,6 +26,10 @@ export const ConnectToSeeRewards = () => {
<Button <Button
data-testid="connect-to-vega-wallet-btn" data-testid="connect-to-vega-wallet-btn"
onClick={() => { onClick={() => {
appDispatch({
type: AppStateActionType.SET_VEGA_WALLET_OVERLAY,
isOpen: true,
});
openVegaWalletDialog(); openVegaWalletDialog();
}} }}
> >
@@ -8,9 +8,10 @@ import { TxState } from '../../../hooks/transaction-reducer';
import { useTransaction } from '../../../hooks/use-transaction'; import { useTransaction } from '../../../hooks/use-transaction';
import { BigNumber } from '../../../lib/bignumber'; import { BigNumber } from '../../../lib/bignumber';
import { AssociateInfo } from './associate-info'; import { AssociateInfo } from './associate-info';
import { removeDecimal, toBigNum } from '@vegaprotocol/utils'; import { toBigNum } from '@vegaprotocol/utils';
import type { EthereumConfig } from '@vegaprotocol/web3'; import type { EthereumConfig } from '@vegaprotocol/web3';
import { useBalances } from '../../../lib/balances/balances-store'; import { useBalances } from '../../../lib/balances/balances-store';
import { MaxUint256 } from '@ethersproject/constants';
export const WalletAssociate = ({ export const WalletAssociate = ({
perform, perform,
@@ -42,7 +43,7 @@ export const WalletAssociate = ({
} = useTransaction(() => { } = useTransaction(() => {
return token.approve( return token.approve(
ethereumConfig.staking_bridge_contract.address, ethereumConfig.staking_bridge_contract.address,
removeDecimal('1000000', decimals).toString() MaxUint256.toString()
); );
}); });
@@ -216,7 +216,7 @@ export const StakingForm = ({
text: t('associateVegaNow'), text: t('associateVegaNow'),
action: () => navigate(Routes.ASSOCIATE), action: () => navigate(Routes.ASSOCIATE),
className: 'py-1', className: 'py-1',
size: 'small', size: 'sm',
}} }}
/> />
)} )}
+17 -14
View File
@@ -2,6 +2,7 @@ import { removeDecimal } from '@vegaprotocol/cypress';
import * as Schema from '@vegaprotocol/types'; import * as Schema from '@vegaprotocol/types';
import { import {
OrderStatusMapping, OrderStatusMapping,
OrderTimeInForceMapping,
OrderTypeMapping, OrderTypeMapping,
Side, Side,
} from '@vegaprotocol/types'; } from '@vegaprotocol/types';
@@ -16,6 +17,7 @@ const orderStatus = 'status';
const orderRemaining = 'remaining'; const orderRemaining = 'remaining';
const orderPrice = 'price'; const orderPrice = 'price';
const orderTimeInForce = 'timeInForce'; const orderTimeInForce = 'timeInForce';
const orderCreatedAt = 'createdAt';
const orderUpdatedAt = 'updatedAt'; const orderUpdatedAt = 'updatedAt';
const assetSelectField = 'select[name="asset"]'; const assetSelectField = 'select[name="asset"]';
const amountField = 'input[name="amount"]'; const amountField = 'input[name="amount"]';
@@ -54,7 +56,7 @@ describe('capsule - without MultiSign', { tags: '@slow' }, () => {
it('can deposit', function () { it('can deposit', function () {
cy.visit('/#/portfolio'); cy.visit('/#/portfolio');
cy.get('[data-testid="pathname-/portfolio"]').should('exist'); cy.get('main[data-testid="/portfolio"]').should('exist');
// 1001-DEPO-001 // 1001-DEPO-001
// 1001-DEPO-002 // 1001-DEPO-002
@@ -117,10 +119,10 @@ describe('capsule - without MultiSign', { tags: '@slow' }, () => {
it('can key to key transfers', function () { it('can key to key transfers', function () {
// 1003-TRAN-023 // 1003-TRAN-023
// 1003-TRAN-006 // 1003-TRAN-006
cy.get('[data-testid="pathname-/portfolio"]').should('exist'); cy.get('main[data-testid="/portfolio"]').should('exist');
cy.getByTestId(collateralTab).click(); cy.getByTestId(collateralTab).click();
cy.getByTestId('open-transfer').click(); cy.getByTestId('open-transfer-dialog').click();
cy.getByTestId('transfer-form').should('be.visible'); cy.getByTestId('transfer-form').should('be.visible');
cy.getByTestId('transfer-form').find('[name="toAddress"]').select(1); cy.getByTestId('transfer-form').find('[name="toAddress"]').select(1);
cy.get('select option') cy.get('select option')
@@ -187,21 +189,19 @@ describe('capsule', { tags: '@slow', testIsolation: true }, () => {
// 0006-NETW-010 // 0006-NETW-010
const market = this.market; const market = this.market;
cy.visit(`/#/markets/${market.id}`); cy.visit(`/#/markets/${market.id}`);
cy.getByTestId('node-health-trigger').realHover();
cy.getByTestId('node-health') cy.getByTestId('node-health')
.children() .children()
.first() .first()
.should('contain.text', 'Operational') .should('contain.text', 'Operational')
.next()
.should('contain.text', new URL(Cypress.env('VEGA_URL')).hostname)
.next()
.then(($el) => { .then(($el) => {
const blockHeight = parseInt($el.text()); const blockHeight = parseInt($el.text());
// block height will increase over the course of the test run so best // block height will increase over the course of the test run so best
// we can do here is check that its showing something sensible // we can do here is check that its showing something sensible
expect(blockHeight).to.be.greaterThan(0); expect(blockHeight).to.be.greaterThan(0);
}); });
cy.getByTestId('node-health')
.children()
.eq(1)
.should('contain.text', new URL(Cypress.env('VEGA_URL')).hostname);
}); });
it('can place and receive an order', function () { it('can place and receive an order', function () {
@@ -260,7 +260,10 @@ describe('capsule', { tags: '@slow', testIsolation: true }, () => {
OrderStatusMapping.STATUS_ACTIVE OrderStatusMapping.STATUS_ACTIVE
); );
cy.get(`[col-id='${orderRemaining}']`).should('contain.text', '0.00'); cy.get(`[col-id='${orderRemaining}']`).should(
'contain.text',
`0.00/${order.size}`
);
cy.get(`[col-id='${orderPrice}']`).then(($price) => { cy.get(`[col-id='${orderPrice}']`).then(($price) => {
expect(parseFloat($price.text())).to.equal(parseFloat(order.price)); expect(parseFloat($price.text())).to.equal(parseFloat(order.price));
@@ -268,10 +271,10 @@ describe('capsule', { tags: '@slow', testIsolation: true }, () => {
cy.get(`[col-id='${orderTimeInForce}']`).should( cy.get(`[col-id='${orderTimeInForce}']`).should(
'contain.text', 'contain.text',
'GTC' OrderTimeInForceMapping[order.timeInForce]
); );
checkIfDataAndTimeOfCreationAndUpdateIsEqual(orderUpdatedAt); checkIfDataAndTimeOfCreationAndUpdateIsEqual(orderCreatedAt);
}); });
}); });
}); });
@@ -344,7 +347,7 @@ describe('capsule', { tags: '@slow', testIsolation: true }, () => {
const ethWalletAddress = Cypress.env('ETHEREUM_WALLET_ADDRESS'); const ethWalletAddress = Cypress.env('ETHEREUM_WALLET_ADDRESS');
cy.visit('/#/portfolio'); cy.visit('/#/portfolio');
cy.get('[data-testid="pathname-/portfolio"]').should('exist'); cy.get('main[data-testid="/portfolio"]').should('exist');
cy.getByTestId(toastCloseBtn, txTimeout).click(); cy.getByTestId(toastCloseBtn, txTimeout).click();
cy.getByTestId('Withdrawals').click(); cy.getByTestId('Withdrawals').click();
cy.getByTestId('withdraw-dialog-button').click(); cy.getByTestId('withdraw-dialog-button').click();
@@ -431,7 +434,7 @@ describe('capsule', { tags: '@slow', testIsolation: true }, () => {
// 1001-DEPO-006 // 1001-DEPO-006
// 1001-DEPO-007 // 1001-DEPO-007
cy.visit('/#/portfolio'); cy.visit('/#/portfolio');
cy.get('[data-testid="pathname-/portfolio"]').should('exist'); cy.get('main[data-testid="/portfolio"]').should('exist');
cy.getByTestId(toastCloseBtn, txTimeout).click(); cy.getByTestId(toastCloseBtn, txTimeout).click();
cy.getByTestId(depositsTab).click(); cy.getByTestId(depositsTab).click();
cy.getByTestId('deposit-button').click(); cy.getByTestId('deposit-button').click();
@@ -453,7 +456,7 @@ describe('capsule', { tags: '@slow', testIsolation: true }, () => {
// 1002-WITH-007 // 1002-WITH-007
cy.visit('/#/portfolio'); cy.visit('/#/portfolio');
cy.get('[data-testid="pathname-/portfolio"]', txTimeout).should('exist'); cy.get('main[data-testid="/portfolio"]', txTimeout).should('exist');
cy.getByTestId(toastCloseBtn, txTimeout).click(); cy.getByTestId(toastCloseBtn, txTimeout).click();
cy.getByTestId(depositsTab).click(); cy.getByTestId(depositsTab).click();
cy.getByTestId('deposit-button').click(); cy.getByTestId('deposit-button').click();
@@ -351,6 +351,12 @@ describe('Closed markets', { tags: '@smoke' }, () => {
) )
); );
// 6001-MARK-017
cy.get(rowSelector)
.first()
.find('[col-id="realisedPNL"]')
.should('have.text', '-');
// 6001-MARK-018 // 6001-MARK-018
cy.get(rowSelector) cy.get(rowSelector)
.first() .first()
@@ -363,10 +369,6 @@ describe('Closed markets', { tags: '@smoke' }, () => {
.first() .first()
.find('button svg') .find('button svg')
.should('exist'); .should('exist');
cy.get(rowSelector)
.find('[col-id="successorMarketID"]')
.first()
.should('have.text', ' - ');
}); });
// test market list for market in terminated state // test market list for market in terminated state
@@ -17,11 +17,11 @@ describe('deposit form validation', { tags: '@smoke' }, () => {
cy.mockTradingPage(); cy.mockTradingPage();
cy.setVegaWallet(); cy.setVegaWallet();
cy.visit('/#/portfolio'); cy.visit('/#/portfolio');
cy.get('[data-testid="pathname-/portfolio"]').should('exist'); cy.get('main[data-testid="/portfolio"]').should('exist');
cy.getByTestId('Deposits').click(); cy.getByTestId('Deposits').click();
cy.getByTestId('deposit-button').click(); cy.getByTestId('deposit-button').click();
connectEthereumWallet('MetaMask');
cy.wait('@Assets'); cy.wait('@Assets');
connectEthereumWallet('MetaMask');
} }
before(() => { before(() => {
@@ -102,6 +102,8 @@ describe('deposit actions', { tags: '@smoke' }, () => {
cy.mockSubscription(); cy.mockSubscription();
cy.setVegaWallet(); cy.setVegaWallet();
cy.visit('/#/markets/market-1'); cy.visit('/#/markets/market-1');
cy.wait('@MarketsCandles');
cy.getByTestId('dialog-close').click();
}); });
it('Deposit to trade is visble', () => { it('Deposit to trade is visble', () => {
@@ -1,6 +1,5 @@
const dialogContent = 'dialog-content'; const dialogContent = 'dialog-content';
const nodeHealth = 'node-health'; const nodeHealth = 'node-health';
const nodeHealthTrigger = 'node-health-trigger';
describe('home', { tags: '@regression' }, () => { describe('home', { tags: '@regression' }, () => {
before(() => { before(() => {
@@ -9,23 +8,22 @@ describe('home', { tags: '@regression' }, () => {
cy.visit('/'); cy.visit('/');
}); });
describe('node health', () => { describe('footer', () => {
it('shows current block height', () => { it('shows current block height', () => {
// 0006-NETW-004 // 0006-NETW-004
// 0006-NETW-008 // 0006-NETW-008
// 0006-NETW-009 // 0006-NETW-009
cy.getByTestId(nodeHealthTrigger).realHover();
cy.getByTestId(nodeHealth) cy.getByTestId(nodeHealth)
.children() .children()
.first() .first()
.should('contain.text', 'Operational', { .should('contain.text', 'Operational', {
timeout: 10000, timeout: 10000,
}) })
.next()
.should('contain.text', new URL(Cypress.env('VEGA_URL')).hostname)
.next()
.should('contain.text', '100'); // all mocked queries have x-block-height header set to 100 .should('contain.text', '100'); // all mocked queries have x-block-height header set to 100
cy.getByTestId(nodeHealth)
.children()
.eq(1)
.should('contain.text', new URL(Cypress.env('VEGA_URL')).hostname);
}); });
it('shows node switcher details', () => { it('shows node switcher details', () => {
@@ -34,7 +32,7 @@ describe('home', { tags: '@regression' }, () => {
// 0006-NETW-014 // 0006-NETW-014
// 0006-NETW-015 // 0006-NETW-015
// 0006-NETW-016 // 0006-NETW-016
cy.getByTestId(nodeHealthTrigger).click(); cy.getByTestId(nodeHealth).click();
cy.getByTestId(dialogContent).should('contain.text', 'Connected node'); cy.getByTestId(dialogContent).should('contain.text', 'Connected node');
cy.getByTestId(dialogContent).should( cy.getByTestId(dialogContent).should(
'contain.text', 'contain.text',
@@ -58,7 +56,7 @@ describe('home', { tags: '@regression' }, () => {
// 0006-NETW-018 // 0006-NETW-018
// 0006-NETW-019 // 0006-NETW-019
// 0006-NETW-020 // 0006-NETW-020
cy.getByTestId(nodeHealthTrigger).click(); cy.getByTestId(nodeHealth).click();
cy.getByTestId('connect').should('be.disabled'); cy.getByTestId('connect').should('be.disabled');
cy.getByTestId('node-url-custom').click(); cy.getByTestId('node-url-custom').click();
cy.getByTestId('connect').should('be.disabled'); cy.getByTestId('connect').should('be.disabled');
+1 -1
View File
@@ -106,7 +106,7 @@ describe('home', { tags: '@regression' }, () => {
cy.visit('/'); cy.visit('/');
cy.wait('@Markets'); cy.wait('@Markets');
cy.get('[data-testid^="pathname-/markets/"]'); cy.get('main[data-testid^="/markets/"]');
// the choose market overlay is no longer showing // the choose market overlay is no longer showing
cy.contains('Loading...').should('not.exist'); cy.contains('Loading...').should('not.exist');
@@ -68,15 +68,14 @@ describe('market info is displayed', { tags: '@smoke' }, () => {
validateMarketDataRow(0, 'Name', 'BTCUSD Monthly (30 Jun 2022)'); validateMarketDataRow(0, 'Name', 'BTCUSD Monthly (30 Jun 2022)');
validateMarketDataRow(1, 'Market ID', 'market-0'); validateMarketDataRow(1, 'Market ID', 'market-0');
validateMarketDataRow(2, 'Parent Market ID', 'market-1');
validateMarketDataRow( validateMarketDataRow(
3, 2,
'Trading Mode', 'Trading Mode',
MarketTradingModeMapping.TRADING_MODE_CONTINUOUS MarketTradingModeMapping.TRADING_MODE_CONTINUOUS
); );
validateMarketDataRow(4, 'Market Decimal Places', '5'); validateMarketDataRow(3, 'Market Decimal Places', '5');
validateMarketDataRow(5, 'Position Decimal Places', '0'); validateMarketDataRow(4, 'Position Decimal Places', '0');
validateMarketDataRow(6, 'Settlement Asset Decimal Places', '5'); validateMarketDataRow(5, 'Settlement Asset Decimal Places', '5');
}); });
it('instrument displayed', () => { it('instrument displayed', () => {
@@ -132,10 +132,9 @@ describe('liquidity table view', { tags: '@smoke' }, () => {
it('can see header title', () => { it('can see header title', () => {
// 5002-LIQP-004 // 5002-LIQP-004
// 5002-LIQP-005 // 5002-LIQP-005
cy.getByTestId('header-title').should( cy.getByTestId('header-title')
'contain.text', .should('contain.text', 'BTCUSD.MF21 liquidity provision')
'BTCUSD.MF21 liquidity provision' .and('contain.text', 'Go to trading');
);
}); });
it('can see target stake', () => { it('can see target stake', () => {
@@ -172,7 +171,7 @@ describe('liquidity table view', { tags: '@smoke' }, () => {
cy.getByTestId('liquidity-supplied').within(() => { cy.getByTestId('liquidity-supplied').within(() => {
cy.getByTestId(itemHeader).should('have.text', 'Liquidity supplied'); cy.getByTestId(itemHeader).should('have.text', 'Liquidity supplied');
cy.getByTestId('indicator').should('be.visible'); cy.getByTestId('indicator').should('be.visible');
cy.getByTestId(itemValue).should('have.text', ' 0.10%').realHover(); cy.getByTestId(itemValue).should('have.text', '0.10%').realHover();
}); });
}); });
}); });
@@ -22,12 +22,15 @@ describe('markets selector', { tags: '@smoke' }, () => {
cy.wait('@Markets'); cy.wait('@Markets');
cy.wait('@MarketsData'); cy.wait('@MarketsData');
cy.wait('@MarketsCandles');
}); });
// 6001-MARK-066 // 6001-MARK-066
it('can open popover to view markets', () => { it('can toggle the sidebar', () => {
cy.getByTestId('market-selector').should('be.visible');
cy.getByTestId('sidebar-toggle').click();
cy.getByTestId('market-selector').should('not.exist'); cy.getByTestId('market-selector').should('not.exist');
cy.getByTestId('header-title').should('be.visible').click(); cy.getByTestId('sidebar-toggle').click();
cy.getByTestId('market-selector').should('be.visible'); cy.getByTestId('market-selector').should('be.visible');
}); });
@@ -37,26 +40,29 @@ describe('markets selector', { tags: '@smoke' }, () => {
const data = [ const data = [
{ {
code: 'SOLUSD', code: 'SOLUSD',
markPrice: '84.41', markPrice: '84.41XYZalpha',
vol: '0.00', change: '',
vol: '0.0024h vol',
}, },
{ {
code: 'ETHBTC.QM21', code: 'ETHBTC.QM21',
markPrice: '46,126.90058', markPrice: '46,126.90058tBTC',
vol: '0.00', change: '',
vol: '0.0024h vol',
}, },
{ {
code: 'BTCUSD.MF21', code: 'BTCUSD.MF21',
markPrice: '46,126.90058', markPrice: '46,126.90058tDAI',
vol: '0.00', change: '',
vol: '0.0024h vol',
}, },
{ {
code: 'AAPL.MF21', code: 'AAPL.MF21',
markPrice: '46,126.90058', markPrice: '46,126.90058tUSDC',
vol: '0.00', change: '',
vol: '0.0024h vol',
}, },
]; ];
cy.getByTestId('header-title').should('be.visible').click();
cy.getByTestId(list) cy.getByTestId(list)
.find('a') .find('a')
.each((item, i) => { .each((item, i) => {
@@ -65,20 +71,33 @@ describe('markets selector', { tags: '@smoke' }, () => {
// 6001-MARK-022 // 6001-MARK-022
expect(item.find('h3').text()).equals(market.code); expect(item.find('h3').text()).equals(market.code);
expect( expect(
item.find('[data-testid="market-selector-volume"]').text() item.find('[data-testid="market-selector-data-row"]').eq(0).text()
).contains(market.vol); ).contains(market.vol);
// 6001-MARK-024 // 6001-MARK-024
expect( expect(
item.find('[data-testid="market-selector-price"]').text() item.find('[data-testid="market-selector-data-row"]').eq(1).text()
).contains(market.markPrice); ).contains(market.markPrice);
// 6001-MARK-023
expect(item.find('[data-testid="market-item-change"]').text()).equals(
market.change
);
// 6001-MARK-025 // 6001-MARK-025
expect(item.find('[data-testid="sparkline-svg"]')).to.not.exist; expect(item.find('[data-testid="sparkline-svg"]')).to.not.exist;
}); });
}); });
it('can use the filter options', () => { it('can see all markets link', () => {
cy.getByTestId('header-title').should('be.visible').click(); // 6001-MARK-026
cy.getByTestId('market-selector').within(() => {
cy.getByTestId('all-markets-link')
.should('be.visible')
.and('have.text', 'All markets')
.and('have.attr', 'href')
.and('contain', '#/markets/all');
});
});
it('can use the filter options', () => {
// 6001-MARK-027 // 6001-MARK-027
// product type // product type
cy.getByTestId('product-Spot').click(); cy.getByTestId('product-Spot').click();
@@ -99,8 +118,6 @@ describe('markets selector', { tags: '@smoke' }, () => {
}); });
it('can sort by by top gaining and top losing market', () => { it('can sort by by top gaining and top losing market', () => {
cy.getByTestId('header-title').should('be.visible').click();
// 6001-MARK-030 // 6001-MARK-030
// 6001-MARK-031 // 6001-MARK-031
// 6001-MARK-032 // 6001-MARK-032
@@ -118,8 +135,6 @@ describe('markets selector', { tags: '@smoke' }, () => {
}); });
it('can filter by settlement asset', () => { it('can filter by settlement asset', () => {
cy.getByTestId('header-title').should('be.visible').click();
// 6001-MARK-028 // 6001-MARK-028
cy.getByTestId('asset-trigger').click(); cy.getByTestId('asset-trigger').click();
cy.getByTestId('asset-id-asset-3').contains('tBTC').click(); cy.getByTestId('asset-id-asset-3').contains('tBTC').click();
@@ -0,0 +1,71 @@
describe('market bottom panel', { tags: '@smoke' }, () => {
before(() => {
cy.clearAllLocalStorage();
cy.mockTradingPage();
cy.mockSubscription();
cy.visit('/#/markets/market-0');
cy.wait('@MarketData');
});
it('on xxl screen should be splitted out into two tables', () => {
cy.getByTestId('tab-positions').should('have.attr', 'data-state', 'active');
cy.getByTestId('tab-open-orders').should(
'have.attr',
'data-state',
'inactive'
);
cy.getByTestId('tab-closed-orders').should(
'have.attr',
'data-state',
'inactive'
);
cy.getByTestId('tab-rejected-orders').should(
'have.attr',
'data-state',
'inactive'
);
cy.getByTestId('tab-orders').should('have.attr', 'data-state', 'inactive');
cy.getByTestId('tab-fills').should('have.attr', 'data-state', 'inactive');
cy.getByTestId('tab-accounts').should(
'have.attr',
'data-state',
'inactive'
);
cy.viewport(1801, 1000);
cy.getByTestId('tab-positions').should('have.attr', 'data-state', 'active');
cy.getByTestId('tab-open-orders').should(
'have.attr',
'data-state',
'inactive'
);
cy.getByTestId('tab-closed-orders').should(
'have.attr',
'data-state',
'inactive'
);
cy.getByTestId('tab-rejected-orders').should(
'have.attr',
'data-state',
'inactive'
);
cy.getByTestId('tab-orders').should('have.attr', 'data-state', 'inactive');
cy.getByTestId('tab-fills').should('have.attr', 'data-state', 'inactive');
cy.getByTestId('tab-accounts').should(
'have.attr',
'data-state',
'inactive'
);
cy.getByTestId('Fills').click();
cy.getByTestId('Collateral').click();
cy.getByTestId('tab-positions').should(
'have.attr',
'data-state',
'inactive'
);
cy.getByTestId('tab-orders').should('have.attr', 'data-state', 'inactive');
cy.getByTestId('tab-fills').should('have.attr', 'data-state', 'active');
cy.getByTestId('tab-accounts').should('have.attr', 'data-state', 'active');
});
});
@@ -99,6 +99,9 @@ describe('Navbar', { tags: '@smoke' }, () => {
cy.getByTestId('menu-drawer').should('not.be.visible'); cy.getByTestId('menu-drawer').should('not.be.visible');
cy.getByTestId('button-menu-drawer').click(); cy.getByTestId('button-menu-drawer').click();
cy.getByTestId('menu-drawer').should('be.visible'); cy.getByTestId('menu-drawer').should('be.visible');
cy.getByTestId('menu-drawer')
.find('[data-testid="Settings"]')
.should('be.visible');
cy.getByTestId('button-menu-drawer').click(); cy.getByTestId('button-menu-drawer').click();
cy.getByTestId('menu-drawer').should('not.be.visible'); cy.getByTestId('menu-drawer').should('not.be.visible');
}); });
@@ -1,15 +1,14 @@
const orderbookTab = 'Orderbook'; const orderbookTab = 'Orderbook';
const orderbookTable = 'tab-orderbook'; const orderbookTable = 'tab-orderbook';
const askPrice = 'price-9894185'; const askPrice = 'price-9894585';
const bidPrice = 'price-9889001'; const bidPrice = 'price-9889001';
const askVolume = 'ask-vol-9894185'; const askVolume = 'ask-vol-9894585';
const bidVolume = 'bid-vol-9889001'; const bidVolume = 'bid-vol-9889001';
const askCumulative = 'cumulative-vol-9894185'; const askCumulative = 'cumulative-vol-9894585';
const bidCumulative = 'cumulative-vol-9889001'; const bidCumulative = 'cumulative-vol-9889001';
const midPrice = 'middle-mark-price-4612690000'; const midPrice = 'middle-mark-price-4612690000';
const priceResolution = 'resolution'; const priceResolution = 'resolution';
const dealTicketPrice = 'order-price'; const dealTicketPrice = 'order-price';
const dealTicketSize = 'order-size';
const resPrice = 'price-990'; const resPrice = 'price-990';
describe('order book', { tags: '@smoke' }, () => { describe('order book', { tags: '@smoke' }, () => {
@@ -34,7 +33,7 @@ describe('order book', { tags: '@smoke' }, () => {
it('show orders prices', () => { it('show orders prices', () => {
// 6003-ORDB-003 // 6003-ORDB-003
cy.getByTestId(askPrice).should('have.text', '98.94185'); cy.getByTestId(askPrice).should('have.text', '98.94585');
cy.getByTestId(bidPrice).should('have.text', '98.89001'); cy.getByTestId(bidPrice).should('have.text', '98.89001');
}); });
@@ -46,7 +45,7 @@ describe('order book', { tags: '@smoke' }, () => {
it('show prices cumulative volumes', () => { it('show prices cumulative volumes', () => {
// 6003-ORDB-005 // 6003-ORDB-005
cy.getByTestId(askCumulative).should('have.text', '38'); cy.getByTestId(askCumulative).should('have.text', '39');
cy.getByTestId(bidCumulative).should('have.text', '7'); cy.getByTestId(bidCumulative).should('have.text', '7');
}); });
@@ -72,19 +71,7 @@ describe('order book', { tags: '@smoke' }, () => {
it('copy price to deal ticket form', () => { it('copy price to deal ticket form', () => {
// 6003-ORDB-009 // 6003-ORDB-009
cy.getByTestId(askPrice).click(); cy.getByTestId(askPrice).click();
cy.getByTestId(dealTicketPrice).should('have.value', '98.94185'); cy.getByTestId(dealTicketPrice).should('have.value', '98.94585');
});
it('copy size to deal ticket form', () => {
// 6003-ORDB-009
cy.getByTestId(bidCumulative).click();
cy.getByTestId(dealTicketSize).should('have.value', '7');
});
it('copy size to deal ticket form', () => {
// 6003-ORDB-009
cy.getByTestId(bidVolume).click();
cy.getByTestId(dealTicketSize).should('have.value', '1');
}); });
it('change price resolution', () => { it('change price resolution', () => {
@@ -101,14 +88,13 @@ describe('order book', { tags: '@smoke' }, () => {
'1,000', '1,000',
'10,000', '10,000',
]; ];
cy.getByTestId(priceResolution).click(); cy.getByTestId(priceResolution)
cy.get('[role="menu"]') .find('option')
.find('[role="menuitem"]')
.each(($el, index) => { .each(($el, index) => {
expect($el.text()).to.equal(resolutions[index]); expect($el.text()).to.equal(resolutions[index]);
}); });
cy.get('[role="menuitem"]').eq(4).click(); cy.getByTestId(priceResolution).select('0.0');
cy.getByTestId(resPrice).should('have.text', '99.0'); cy.getByTestId(resPrice).should('have.text', '99.0');
cy.getByTestId(askPrice).should('not.exist'); cy.getByTestId(askPrice).should('not.exist');
cy.getByTestId(bidPrice).should('not.exist'); cy.getByTestId(bidPrice).should('not.exist');
+33 -20
View File
@@ -1,29 +1,42 @@
describe('Settings page', { tags: '@smoke' }, () => { describe('Settings page', { tags: '@smoke' }, () => {
beforeEach(() => { beforeEach(() => {
cy.clearLocalStorage(); cy.clearLocalStorage().then(() => {
cy.mockTradingPage();
cy.mockTradingPage(); cy.mockSubscription();
cy.mockSubscription(); cy.visit('/');
cy.visit('/'); cy.get('[aria-label="cog icon"]').click();
// Only click if not already active otherwise sidebar will close
cy.get('[data-testid="sidebar-content"]').then(($sidebarContent) => {
if ($sidebarContent.find('h2').text() !== 'Settings') {
cy.get('[data-testid="sidebar"] [data-testid="Settings"]').click();
}
}); });
}); });
it('telemetry checkbox should work well', () => { it('telemetry checkbox should work well', () => {
const telemetrySwitch = '#switch-settings-telemetry-switch'; cy.location('hash').should('equal', '#/settings');
cy.get(telemetrySwitch).should('have.attr', 'data-state', 'unchecked'); cy.getByTestId('telemetry-approval').should(
cy.get(telemetrySwitch).click(); 'have.attr',
cy.get(telemetrySwitch).should('have.attr', 'data-state', 'checked'); 'data-state',
'unchecked'
);
cy.get('[for="telemetry-approval"]').click();
cy.getByTestId('telemetry-approval').should(
'have.attr',
'data-state',
'checked'
);
cy.reload(); cy.reload();
cy.get(telemetrySwitch).should('have.attr', 'data-state', 'checked'); cy.getByTestId('telemetry-approval').should(
cy.get(telemetrySwitch).click(); 'have.attr',
cy.get(telemetrySwitch).should('have.attr', 'data-state', 'unchecked'); 'data-state',
'checked'
);
cy.get('[for="telemetry-approval"]').click();
cy.getByTestId('telemetry-approval').should(
'have.attr',
'data-state',
'unchecked'
);
cy.reload(); cy.reload();
cy.get(telemetrySwitch).should('have.attr', 'data-state', 'unchecked'); cy.getByTestId('telemetry-approval').should(
'have.attr',
'data-state',
'unchecked'
);
}); });
}); });
@@ -14,12 +14,6 @@ describe('deal ticker order validation', { tags: '@smoke' }, () => {
cy.mockSubscription(); cy.mockSubscription();
cy.visit('/#/markets/market-0'); cy.visit('/#/markets/market-0');
cy.wait('@Markets'); cy.wait('@Markets');
cy.get('[data-testid="deal-ticket-form"]').then(($form) => {
if (!$form.length) {
cy.getByTestId('Order').click();
}
});
}); });
beforeEach(() => { beforeEach(() => {
@@ -12,7 +12,7 @@ describe(
'account validation', 'account validation',
{ tags: '@regression', testIsolation: true }, { tags: '@regression', testIsolation: true },
() => { () => {
describe.skip('zero balance error', () => { describe('zero balance error', () => {
beforeEach(() => { beforeEach(() => {
cy.setVegaWallet(); cy.setVegaWallet();
cy.mockTradingPage(); cy.mockTradingPage();
@@ -59,12 +59,6 @@ describe(
cy.mockSubscription(); cy.mockSubscription();
cy.visit('/#/markets/market-0'); cy.visit('/#/markets/market-0');
cy.wait('@Markets'); cy.wait('@Markets');
cy.get('[data-testid="deal-ticket-form"]').then(($form) => {
if (!$form.length) {
cy.getByTestId('Order').click();
}
});
}); });
it('should display info and button for deposit', () => { it('should display info and button for deposit', () => {
@@ -80,8 +74,8 @@ describe(
'You may not have enough margin available to open this position. 5.00 tDAI is currently required. You have only 0.01001 tDAI available.' 'You may not have enough margin available to open this position. 5.00 tDAI is currently required. You have only 0.01001 tDAI available.'
); );
cy.getByTestId('deal-ticket-deposit-dialog-button').click(); cy.getByTestId('deal-ticket-deposit-dialog-button').click();
cy.getByTestId('sidebar-content') cy.getByTestId('dialog-content')
.find('h2') .find('h1')
.eq(0) .eq(0)
.should('have.text', 'Deposit'); .should('have.text', 'Deposit');
}); });
@@ -37,7 +37,7 @@ describe('fills', { tags: '@regression' }, () => {
it('renders fills on portfolio page', () => { it('renders fills on portfolio page', () => {
cy.visit('/#/portfolio'); cy.visit('/#/portfolio');
cy.get('[data-testid="pathname-/portfolio"]').should('exist'); cy.get('main[data-testid="/portfolio"]').should('exist');
cy.getByTestId('Fills').click(); cy.getByTestId('Fills').click();
validateFillsDisplayed(); validateFillsDisplayed();
}); });
@@ -431,8 +431,6 @@ describe('amend and cancel order', { tags: '@smoke' }, () => {
}); });
const orderId = '1234567890'; const orderId = '1234567890';
// this test is flakey
it('must be able to amend the price of an order', () => { it('must be able to amend the price of an order', () => {
// 7003-MORD-007 // 7003-MORD-007
// 7003-MORD-012 // 7003-MORD-012
@@ -50,7 +50,7 @@ describe('Portfolio page', { tags: '@smoke' }, () => {
cy.get('fieldset.ag-simple-filter-body-wrapper') cy.get('fieldset.ag-simple-filter-body-wrapper')
.should('be.visible') .should('be.visible')
.within((fields) => { .within((fields) => {
cy.wrap(fields).find('label').should('have.length', 18); cy.wrap(fields).find('label').should('have.length', 16);
}); });
cy.getByTestId('"Ledger entries"').click(); cy.getByTestId('"Ledger entries"').click();
cy.get('fieldset.ag-simple-filter-body-wrapper').should('not.exist'); cy.get('fieldset.ag-simple-filter-body-wrapper').should('not.exist');
@@ -10,7 +10,6 @@ describe('trades', { tags: '@smoke' }, () => {
cy.mockTradingPage(); cy.mockTradingPage();
cy.mockSubscription(); cy.mockSubscription();
}); });
before(() => { before(() => {
cy.mockTradingPage(); cy.mockTradingPage();
cy.mockSubscription(); cy.mockSubscription();
@@ -28,50 +27,37 @@ describe('trades', { tags: '@smoke' }, () => {
it('show trades prices', () => { it('show trades prices', () => {
// 6005-THIS-003 // 6005-THIS-003
cy.getByTestId(tradesTable) cy.get(`${colIdPrice} ${colHeader}`).first().should('have.text', 'Price');
.get(`${colIdPrice} ${colHeader}`) cy.get(colIdPrice).each(($tradePrice) => {
.first() cy.wrap($tradePrice).invoke('text').should('not.be.empty');
.should('have.text', 'Price'); });
cy.getByTestId(tradesTable)
.get(colIdPrice)
.each(($tradePrice) => {
cy.wrap($tradePrice).invoke('text').should('not.be.empty');
});
}); });
it('show trades sizes', () => { it('show trades sizes', () => {
// 6005-THIS-004 // 6005-THIS-004
cy.getByTestId(tradesTable) cy.get(`${colIdSize} ${colHeader}`).first().should('have.text', 'Size');
.get(`${colIdSize} ${colHeader}`) cy.get(colIdSize).each(($tradeSize) => {
.first() cy.wrap($tradeSize).invoke('text').should('not.be.empty');
.should('have.text', 'Size'); });
cy.getByTestId(tradesTable)
.get(colIdSize)
.each(($tradeSize) => {
cy.wrap($tradeSize).invoke('text').should('not.be.empty');
});
}); });
// This won't pass in CI, but does locally it('show trades date and time', () => {
it.skip('show trades date and time', () => {
// 6005-THIS-005 // 6005-THIS-005
cy.getByTestId(tradesTable) // order table shares identical col id cy.get(`${colIdCreatedAt} ${colHeader}`).should('have.text', 'Created at');
.find(`${colIdCreatedAt} ${colHeader}`)
.should('have.text', 'Created at');
const dateTimeRegex = const dateTimeRegex =
/(\d{1,2})\/(\d{1,2})\/(\d{4}), (\d{1,2}):(\d{1,2}):(\d{1,2})/gm; /(\d{1,2})\/(\d{1,2})\/(\d{4}), (\d{1,2}):(\d{1,2}):(\d{1,2})/gm;
cy.getByTestId(tradesTable) cy.get(colIdCreatedAt).each(($tradeDateTime, index) => {
.get(`.ag-center-cols-container ${colIdCreatedAt}`) if (index != 0) {
.each(($tradeDateTime) => { //ignore header
cy.wrap($tradeDateTime).invoke('text').should('match', dateTimeRegex); cy.wrap($tradeDateTime).invoke('text').should('match', dateTimeRegex);
}); }
});
}); });
it('trades are sorted descending by datetime', () => { it('trades are sorted descending by datetime', () => {
// 6005-THIS-006 // 6005-THIS-006
const dateTimes: Date[] = []; const dateTimes: Date[] = [];
cy.getByTestId(tradesTable) cy.get(colIdCreatedAt)
.find(colIdCreatedAt)
.each(($tradeDateTime, index) => { .each(($tradeDateTime, index) => {
if (index != 0) { if (index != 0) {
//ignore header //ignore header
@@ -85,15 +71,9 @@ describe('trades', { tags: '@smoke' }, () => {
}); });
}); });
// this passes locally but doesn't in CI it('copy price to deal ticket form', () => {
it.skip('copy price to deal ticket form', () => {
cy.getByTestId('order-type-TYPE_LIMIT').click(); // make sure on limit
// 6005-THIS-007 // 6005-THIS-007
cy.getByTestId(tradesTable) cy.get(colIdPrice).last().should('be.visible').click();
.find(colIdPrice)
.last()
.should('be.visible')
.click();
cy.getByTestId('order-price').should('have.value', '171.16898'); cy.getByTestId('order-price').should('have.value', '171.16898');
}); });
}); });
@@ -10,7 +10,7 @@ describe('ethereum wallet', { tags: '@smoke', testIsolation: true }, () => {
cy.mockSubscription(); cy.mockSubscription();
cy.setVegaWallet(); cy.setVegaWallet();
cy.visit('/#/portfolio'); cy.visit('/#/portfolio');
cy.get('[data-testid="pathname-/portfolio"]').should('exist'); cy.get('main[data-testid="/portfolio"]').should('exist');
cy.getByTestId('Withdrawals').click(); cy.getByTestId('Withdrawals').click();
}); });
@@ -17,7 +17,7 @@ describe(
cy.visit('/#/portfolio'); cy.visit('/#/portfolio');
cy.mockTradingPage(); cy.mockTradingPage();
cy.mockSubscription(); cy.mockSubscription();
cy.get('[data-testid="pathname-/portfolio"]').should('exist'); cy.get('main[data-testid="/portfolio"]').should('exist');
}); });
it('can connect', () => { it('can connect', () => {
@@ -63,7 +63,7 @@ describe(
cy.contains('Hosted Fairground wallet'); cy.contains('Hosted Fairground wallet');
cy.getByTestId('connectors-list') cy.getByTestId('connectors-list')
.find('[data-testid="connector-rest"]') .find('[data-testid="connector-hosted"]')
.click(); .click();
cy.getByTestId(form).find('#wallet').click().type('user'); cy.getByTestId(form).find('#wallet').click().type('user');
cy.getByTestId(form).find('#passphrase').click().type('pass'); cy.getByTestId(form).find('#passphrase').click().type('pass');
@@ -89,7 +89,7 @@ describe(
); );
cy.getByTestId(connectVegaBtn).click(); cy.getByTestId(connectVegaBtn).click();
cy.getByTestId('connectors-list') cy.getByTestId('connectors-list')
.find('[data-testid="connector-rest"]') .find('[data-testid="connector-hosted"]')
.click(); .click();
cy.getByTestId(form).find('#wallet').click().type('invalid name'); cy.getByTestId(form).find('#wallet').click().type('invalid name');
cy.getByTestId(form).find('#passphrase').click().type('invalid password'); cy.getByTestId(form).find('#passphrase').click().type('invalid password');
@@ -100,7 +100,7 @@ describe(
it('doesnt connect with empty fields', () => { it('doesnt connect with empty fields', () => {
cy.getByTestId(connectVegaBtn).click(); cy.getByTestId(connectVegaBtn).click();
cy.getByTestId('connectors-list') cy.getByTestId('connectors-list')
.find('[data-testid="connector-rest"]') .find('[data-testid="connector-hosted"]')
.click(); .click();
cy.getByTestId('rest-connector-form').find('button[type=submit]').click(); cy.getByTestId('rest-connector-form').find('button[type=submit]').click();
@@ -122,7 +122,7 @@ describe('connect vega wallet', { tags: '@smoke', testIsolation: true }, () => {
cy.visit('/#/portfolio'); cy.visit('/#/portfolio');
cy.mockTradingPage(); cy.mockTradingPage();
cy.mockSubscription(); cy.mockSubscription();
cy.get('[data-testid="pathname-/portfolio"]').should('exist'); cy.get('main[data-testid="/portfolio"]').should('exist');
}); });
it('can connect', () => { it('can connect', () => {
@@ -1,20 +1,24 @@
import { selectAsset } from '../support/helpers'; import { selectAsset } from '../support/helpers';
// #region consts
const amountField = 'input[name="amount"]'; const amountField = 'input[name="amount"]';
const amountShortName = 'input[name="amount"] + div + span.text-xs'; const amountShortName = 'input[name="amount"] + div + span.text-xs';
const assetSelection = 'select-asset'; const assetSelection = 'select-asset';
const assetBalance = 'asset-balance'; const assetBalance = 'asset-balance';
const assetOption = 'rich-select-option'; const assetOption = 'rich-select-option';
const transferText = 'transfer-intro-text'; const closeDialog = 'dialog-close';
const dialogTitle = 'dialog-title';
const dialogTransferText = 'dialog-transfer-text';
const dropdownMenu = 'dropdown-menu';
const errorText = 'input-error-text'; const errorText = 'input-error-text';
const formFieldError = 'input-error-text'; const formFieldError = 'input-error-text';
const includeTransferFeeRadioBtn = 'include-transfer-fee'; const includeTransferFeeRadioBtn = 'include-transfer-fee';
const keyID = `[data-testid="${transferText}"] > .rounded-md`; const keyID = '[data-testid="dialog-transfer-text"] > .rounded-md';
const manageVegaWallet = 'manage-vega-wallet'; const manageVegaWallet = 'manage-vega-wallet';
const openTransferButton = 'open-transfer'; const openTransferDialog = 'open-transfer-dialog';
const submitTransferBtn = '[type="submit"]'; const submitTransferBtn = '[type="submit"]';
const toAddressField = '[name="toAddress"]'; const toAddressField = '[name="toAddress"]';
const totalTransferfee = 'total-transfer-fee'; const totalTransferfee = 'total-transfer-fee';
const transfer = 'transfer';
const transferAmount = 'transfer-amount'; const transferAmount = 'transfer-amount';
const transferForm = 'transfer-form'; const transferForm = 'transfer-form';
const transferFee = 'transfer-fee'; const transferFee = 'transfer-fee';
@@ -26,6 +30,7 @@ const ASSET_SEPOLIA_TBTC = 2;
const collateralTab = 'Collateral'; const collateralTab = 'Collateral';
const toastCloseBtn = 'toast-close'; const toastCloseBtn = 'toast-close';
const toastContent = 'toast-content'; const toastContent = 'toast-content';
// #endregion
describe('transfer fees', { tags: '@regression', testIsolation: true }, () => { describe('transfer fees', { tags: '@regression', testIsolation: true }, () => {
beforeEach(() => { beforeEach(() => {
@@ -34,19 +39,15 @@ describe('transfer fees', { tags: '@regression', testIsolation: true }, () => {
cy.mockSubscription(); cy.mockSubscription();
cy.setVegaWallet(); cy.setVegaWallet();
cy.visit('/'); cy.visit('/#/portfolio');
cy.getByTestId('Trading').first().click();
cy.getByTestId(collateralTab).click();
cy.getByTestId(dropdownMenu).first().click();
cy.getByTestId(transfer).click();
cy.wait('@Assets');
cy.wait('@Accounts'); cy.wait('@Accounts');
cy.wait('@Assets');
cy.mockVegaWalletTransaction(); cy.mockVegaWalletTransaction();
// Only click if not already active otherwise sidebar will close
cy.get('[data-testid="sidebar-content"]').then(($sidebarContent) => {
if ($sidebarContent.find('h2').text() !== 'Transfer') {
cy.get('[data-testid="sidebar"] [data-testid="Transfer"]').click();
}
});
}); });
it('transfer fees tooltips', () => { it('transfer fees tooltips', () => {
@@ -71,18 +72,21 @@ describe('transfer fees', { tags: '@regression', testIsolation: true }, () => {
cy.get('[data-side="bottom"] div') cy.get('[data-side="bottom"] div')
.should('be.visible') .should('be.visible')
.should('not.be.empty'); .should('not.be.empty');
cy.getByTestId(dialogTitle).click();
//Check Transfer Fee tooltip //Check Transfer Fee tooltip
cy.contains('div', 'Transfer fee').realHover(); cy.contains('div', 'Transfer fee').realHover();
cy.get('[data-side="bottom"] div') cy.get('[data-side="bottom"] div')
.should('be.visible') .should('be.visible')
.should('not.be.empty'); .should('not.be.empty');
cy.getByTestId(dialogTitle).click();
//Check Amount to be transferred tooltip //Check Amount to be transferred tooltip
cy.contains('div', 'Amount to be transferred').realHover(); cy.contains('div', 'Amount to be transferred').realHover();
cy.get('[data-side="bottom"] div') cy.get('[data-side="bottom"] div')
.should('be.visible') .should('be.visible')
.should('not.be.empty'); .should('not.be.empty');
cy.getByTestId(dialogTitle).click();
//Check Total amount (with fee) tooltip //Check Total amount (with fee) tooltip
cy.contains('div', 'Total amount (with fee)').realHover(); cy.contains('div', 'Total amount (with fee)').realHover();
@@ -130,7 +134,6 @@ describe('transfer fees', { tags: '@regression', testIsolation: true }, () => {
.should('contain.text', '1.00'); .should('contain.text', '1.00');
}); });
}); });
describe( describe(
'transfer form validation', 'transfer form validation',
{ tags: '@regression', testIsolation: true }, { tags: '@regression', testIsolation: true },
@@ -151,7 +154,7 @@ describe(
it('transfer Text', () => { it('transfer Text', () => {
// 1003-TRAN-003 // 1003-TRAN-003
cy.getByTestId(transferText) cy.getByTestId(dialogTransferText)
.should('exist') .should('exist')
.get(keyID) .get(keyID)
.invoke('text') .invoke('text')
@@ -201,6 +204,7 @@ describe(
'contain.text', 'contain.text',
'You cannot transfer more than your available collateral' 'You cannot transfer more than your available collateral'
); );
cy.getByTestId(closeDialog).click();
}); });
} }
); );
@@ -213,7 +217,7 @@ describe('withdraw actions', { tags: '@smoke', testIsolation: true }, () => {
cy.visit('/#/portfolio'); cy.visit('/#/portfolio');
cy.getByTestId(collateralTab).click(); cy.getByTestId(collateralTab).click();
cy.getByTestId(openTransferButton).click(); cy.getByTestId(openTransferDialog).click();
cy.wait('@Accounts'); cy.wait('@Accounts');
cy.wait('@Assets'); cy.wait('@Assets');
@@ -21,7 +21,7 @@ describe('withdraw form validation', { tags: '@smoke' }, () => {
cy.visit('/#/portfolio'); cy.visit('/#/portfolio');
cy.getByTestId('Withdrawals').click(); cy.getByTestId('Withdrawals').click();
cy.getByTestId('Withdraw').click(); // sidebar item cy.getByTestId('withdraw-dialog-button').click();
// It also requires connection Ethereum wallet // It also requires connection Ethereum wallet
connectEthereumWallet('MetaMask'); connectEthereumWallet('MetaMask');
@@ -87,17 +87,15 @@ describe(
cy.setVegaWallet(); cy.setVegaWallet();
cy.visit('/#/portfolio'); cy.visit('/#/portfolio');
cy.wait('@Accounts');
cy.wait('@Assets');
cy.getByTestId('Withdrawals').click(); cy.getByTestId('Withdrawals').click();
cy.getByTestId('Withdraw').click(); cy.getByTestId('withdraw-dialog-button').click();
// It also requires connection Ethereum wallet // It also requires connection Ethereum wallet
connectEthereumWallet('MetaMask'); connectEthereumWallet('MetaMask');
cy.wait('@Accounts');
cy.wait('@Assets');
cy.mockVegaWalletTransaction(); cy.mockVegaWalletTransaction();
}); });
+1 -1
View File
@@ -14,4 +14,4 @@ NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/annou
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
NX_VEGA_CONSOLE_URL=https://console.vega.xyz NX_VEGA_CONSOLE_URL=https://console.vega.xyz
# TAG name of the current app version - TODO: bump to the latest upon release # TAG name of the current app version - TODO: bump to the latest upon release
NX_APP_VERSION=v0.20.21-core-0.71.6 NX_APP_VERSION=v0.20.23-core-0.71.6
+2 -2
View File
@@ -3,9 +3,9 @@ NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.sentry.io/6300613 NX_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.sentry.io/6300613
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/mainnet-mirror/vegawallet-mainnet-mirror.toml NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/mainnet-mirror/vegawallet-mainnet-mirror.toml
NX_VEGA_ENV=MAINNET_MIRROR NX_VEGA_ENV=MAINNET-MIRROR
NX_VEGA_EXPLORER_URL=https://explorer.mainnet-mirror.vega.rocks NX_VEGA_EXPLORER_URL=https://explorer.mainnet-mirror.vega.rocks
NX_VEGA_NETWORKS={\"MAINNET\":\"https://console.vega.xyz\",\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\",\"MAINNET_MIRROR\":\"https://trading.mainnet-mirror.vega.rocks\"} NX_VEGA_NETWORKS={\"MAINNET\":\"https://console.vega.xyz\",\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\",\"MAINNET-MIRROR\":\"https://trading.mainnet-mirror.vega.rocks\"}
NX_VEGA_TOKEN_URL=https://governance.mainnet-mirror.vega.rocks NX_VEGA_TOKEN_URL=https://governance.mainnet-mirror.vega.rocks
NX_VEGA_WALLET_URL=http://localhost:1789 NX_VEGA_WALLET_URL=http://localhost:1789
NX_VEGA_DOCS_URL=https://docs.vega.xyz/mainnet NX_VEGA_DOCS_URL=https://docs.vega.xyz/mainnet
+142 -28
View File
@@ -1,10 +1,36 @@
import { matchFilter, lpAggregatedDataProvider } from '@vegaprotocol/liquidity'; import {
matchFilter,
lpAggregatedDataProvider,
useCheckLiquidityStatus,
} from '@vegaprotocol/liquidity';
import { tooltipMapping } from '@vegaprotocol/markets';
import {
addDecimalsFormatNumber,
formatNumberPercentage,
} from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import {
NetworkParams,
useNetworkParams,
} from '@vegaprotocol/network-parameters';
import { useDataProvider } from '@vegaprotocol/data-provider'; import { useDataProvider } from '@vegaprotocol/data-provider';
import { Tab, Tabs } from '@vegaprotocol/ui-toolkit'; import {
Tab,
Tabs,
Link as UiToolkitLink,
Indicator,
ExternalLink,
} from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet'; import { useVegaWallet } from '@vegaprotocol/wallet';
import { useEffect, useState } from 'react'; import { memo, useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import { Header, HeaderStat, HeaderTitle } from '../../components/header';
import { Link, useParams } from 'react-router-dom';
import { Links, Routes } from '../../pages/client-router';
import { useMarket, useStaticMarketData } from '@vegaprotocol/markets';
import { DocsLinks } from '@vegaprotocol/environment';
import { LiquidityContainer } from '../../components/liquidity-container'; import { LiquidityContainer } from '../../components/liquidity-container';
const enum LiquidityTabs { const enum LiquidityTabs {
@@ -19,6 +45,98 @@ export const Liquidity = () => {
return <LiquidityViewContainer marketId={marketId} />; return <LiquidityViewContainer marketId={marketId} />;
}; };
const LiquidityViewHeader = memo(({ marketId }: { marketId?: string }) => {
const { data: market } = useMarket(marketId);
const { data: marketData } = useStaticMarketData(marketId);
const targetStake = marketData?.targetStake;
const suppliedStake = marketData?.suppliedStake;
const assetDecimalPlaces =
market?.tradableInstrument.instrument.product.settlementAsset.decimals || 0;
const symbol =
market?.tradableInstrument.instrument.product.settlementAsset.symbol;
const { params } = useNetworkParams([
NetworkParams.market_liquidity_stakeToCcyVolume,
NetworkParams.market_liquidity_targetstake_triggering_ratio,
]);
const triggeringRatio =
params.market_liquidity_targetstake_triggering_ratio || '1';
const { percentage, status } = useCheckLiquidityStatus({
suppliedStake: suppliedStake || 0,
targetStake: targetStake || 0,
triggeringRatio,
});
return (
<Header
title={
market?.tradableInstrument.instrument.name &&
market?.tradableInstrument.instrument.code &&
marketId && (
<HeaderTitle
primaryContent={`${market.tradableInstrument.instrument.code} ${t(
'liquidity provision'
)}`}
secondaryContent={
<Link to={Links[Routes.MARKET](marketId)}>
<UiToolkitLink>{t('Go to trading')}</UiToolkitLink>
</Link>
}
/>
)
}
>
<HeaderStat
heading={t('Target stake')}
description={tooltipMapping['targetStake']}
testId="target-stake"
>
<div>
{targetStake
? `${addDecimalsFormatNumber(
targetStake,
assetDecimalPlaces ?? 0
)} ${symbol}`
: '-'}
</div>
</HeaderStat>
<HeaderStat
heading={t('Supplied stake')}
description={tooltipMapping['suppliedStake']}
testId="supplied-stake"
>
<div>
{suppliedStake
? `${addDecimalsFormatNumber(
suppliedStake,
assetDecimalPlaces ?? 0
)} ${symbol}`
: '-'}
</div>
</HeaderStat>
<HeaderStat heading={t('Liquidity supplied')} testId="liquidity-supplied">
<Indicator variant={status} />
{formatNumberPercentage(percentage, 2)}
</HeaderStat>
<HeaderStat heading={t('Market ID')} testId="liquidity-market-id">
<div className="break-word">{marketId}</div>
</HeaderStat>
<HeaderStat heading={t('Learn more')} testId="liquidity-learn-more">
{DocsLinks ? (
<ExternalLink href={DocsLinks.LIQUIDITY}>
{t('Providing liquidity')}
</ExternalLink>
) : (
(null as React.ReactNode)
)}
</HeaderStat>
</Header>
);
});
LiquidityViewHeader.displayName = 'LiquidityViewHeader';
export const LiquidityViewContainer = ({ export const LiquidityViewContainer = ({
marketId, marketId,
}: { }: {
@@ -49,30 +167,26 @@ export const LiquidityViewContainer = ({
}, [data, pubKey]); }, [data, pubKey]);
return ( return (
<div className="h-full p-1.5"> <div className="h-full grid grid-rows-[min-content_1fr]">
<div className="h-full border border-default"> <LiquidityViewHeader marketId={marketId} />
<Tabs value={tab || LiquidityTabs.Active} onValueChange={setTab}> <Tabs value={tab || LiquidityTabs.Active} onValueChange={setTab}>
<Tab <Tab
id={LiquidityTabs.MyLiquidityProvision} id={LiquidityTabs.MyLiquidityProvision}
name={t('My liquidity provision')} name={t('My liquidity provision')}
hidden={!pubKey} hidden={!pubKey}
> >
<LiquidityContainer <LiquidityContainer
marketId={marketId} marketId={marketId}
filter={{ partyId: pubKey || undefined }} filter={{ partyId: pubKey || undefined }}
/> />
</Tab> </Tab>
<Tab id={LiquidityTabs.Active} name={t('Active')}> <Tab id={LiquidityTabs.Active} name={t('Active')}>
<LiquidityContainer marketId={marketId} filter={{ active: true }} /> <LiquidityContainer marketId={marketId} filter={{ active: true }} />
</Tab> </Tab>
<Tab id={LiquidityTabs.Inactive} name={t('Inactive')}> <Tab id={LiquidityTabs.Inactive} name={t('Inactive')}>
<LiquidityContainer <LiquidityContainer marketId={marketId} filter={{ active: false }} />
marketId={marketId} </Tab>
filter={{ active: false }} </Tabs>
/>
</Tab>
</Tabs>
</div>
</div> </div>
); );
}; };
@@ -19,6 +19,7 @@ describe('AssetDropdown', () => {
checkedAssets={[]} checkedAssets={[]}
assets={assets} assets={assets}
onSelect={mockOnSelect} onSelect={mockOnSelect}
onReset={jest.fn()}
/> />
); );
await userEvent.click(screen.getByRole('button')); await userEvent.click(screen.getByRole('button'));
@@ -38,6 +39,7 @@ describe('AssetDropdown', () => {
checkedAssets={[assets[0].id]} checkedAssets={[assets[0].id]}
assets={assets} assets={assets}
onSelect={mockOnSelect} onSelect={mockOnSelect}
onReset={jest.fn()}
/> />
); );
await userEvent.click(screen.getByRole('button')); await userEvent.click(screen.getByRole('button'));
@@ -46,9 +48,35 @@ describe('AssetDropdown', () => {
expect(mockOnSelect).toHaveBeenCalledWith(assets[0].id, false); expect(mockOnSelect).toHaveBeenCalledWith(assets[0].id, false);
}); });
it('can be reset clearing all assets', async () => {
const mockOnSelect = jest.fn();
const mockOnReset = jest.fn();
render(
<AssetDropdown
checkedAssets={assets.map((a) => a.id)}
assets={assets}
onSelect={mockOnSelect}
onReset={mockOnReset}
/>
);
await userEvent.click(screen.getByRole('button'));
const items = screen.getAllByRole('menuitemcheckbox');
// all should be checked
items.forEach((item) => {
expect(item).toBeChecked();
});
await userEvent.click(screen.getByText('Reset'));
expect(mockOnReset).toHaveBeenCalled();
});
it('doesnt render if no assets provided', async () => { it('doesnt render if no assets provided', async () => {
const { container } = render( const { container } = render(
<AssetDropdown checkedAssets={[]} assets={[]} onSelect={jest.fn()} /> <AssetDropdown
checkedAssets={[]}
assets={[]}
onSelect={jest.fn()}
onReset={jest.fn()}
/>
); );
expect(container).toBeEmptyDOMElement(); expect(container).toBeEmptyDOMElement();
}); });
@@ -3,22 +3,22 @@ import {
DropdownMenu, DropdownMenu,
DropdownMenuCheckboxItem, DropdownMenuCheckboxItem,
DropdownMenuContent, DropdownMenuContent,
DropdownMenuItem,
DropdownMenuItemIndicator, DropdownMenuItemIndicator,
DropdownMenuTrigger, DropdownMenuTrigger,
VegaIcon, DropdownMenuSeparator,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit'; } from '@vegaprotocol/ui-toolkit';
type Assets = Array<{ id: string; symbol: string }>;
export const AssetDropdown = ({ export const AssetDropdown = ({
assets, assets,
checkedAssets, checkedAssets,
onSelect, onSelect,
onReset,
}: { }: {
assets: Assets | undefined; assets: Array<{ id: string; symbol: string }> | undefined;
checkedAssets: string[]; checkedAssets: string[];
onSelect: (id: string, checked: boolean) => void; onSelect: (id: string, checked: boolean) => void;
onReset: () => void;
}) => { }) => {
if (!assets?.length) { if (!assets?.length) {
return null; return null;
@@ -28,11 +28,13 @@ export const AssetDropdown = ({
<DropdownMenu <DropdownMenu
trigger={ trigger={
<DropdownMenuTrigger data-testid="asset-trigger"> <DropdownMenuTrigger data-testid="asset-trigger">
<TriggerText assets={assets} checkedAssets={checkedAssets} /> <span className="px-1">$</span>
</DropdownMenuTrigger> </DropdownMenuTrigger>
} }
> >
<DropdownMenuContent> <DropdownMenuContent>
<DropdownMenuItem onClick={onReset}>{t('Reset')}</DropdownMenuItem>
<DropdownMenuSeparator />
{assets?.map((a) => { {assets?.map((a) => {
return ( return (
<DropdownMenuCheckboxItem <DropdownMenuCheckboxItem
@@ -54,27 +56,3 @@ export const AssetDropdown = ({
</DropdownMenu> </DropdownMenu>
); );
}; };
const TriggerText = ({
assets,
checkedAssets,
}: {
assets: Assets;
checkedAssets: string[];
}) => {
let text = t('Assets');
if (checkedAssets.length === 1) {
const assetId = checkedAssets[0];
const asset = assets.find((a) => a.id === assetId);
text = asset ? asset.symbol : t('Asset (1)');
} else if (checkedAssets.length > 1) {
text = t(`${checkedAssets.length} Assets`);
}
return (
<span className="flex justify-between items-center">
{text} <VegaIcon name={VegaIconNames.CHEVRON_DOWN} />
</span>
);
};
@@ -5,85 +5,92 @@ import { MarketProposalNotification } from '@vegaprotocol/proposals';
import type { Market } from '@vegaprotocol/markets'; import type { Market } from '@vegaprotocol/markets';
import { getExpiryDate, getMarketExpiryDate } from '@vegaprotocol/utils'; import { getExpiryDate, getMarketExpiryDate } from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import { Last24hPriceChange, Last24hVolume } from '@vegaprotocol/markets';
import { MarketState as State } from '@vegaprotocol/types';
import { HeaderStat } from '../../components/header'; import { HeaderStat } from '../../components/header';
import { MarketMarkPrice } from '../../components/market-mark-price'; import { MarketMarkPrice } from '../../components/market-mark-price';
import { HeaderStatMarketTradingMode } from '../../components/market-trading-mode'; import { Last24hPriceChange, Last24hVolume } from '@vegaprotocol/markets';
import { MarketState } from '../../components/market-state'; import { MarketState } from '../../components/market-state';
import { HeaderStatMarketTradingMode } from '../../components/market-trading-mode';
import { MarketLiquiditySupplied } from '../../components/liquidity-supplied'; import { MarketLiquiditySupplied } from '../../components/liquidity-supplied';
import { MarketState as State } from '@vegaprotocol/types';
interface MarketHeaderStatsProps { interface HeaderStatsProps {
market: Market | null; market: Market | null;
} }
export const MarketHeaderStats = ({ market }: MarketHeaderStatsProps) => { export const HeaderStats = ({ market }: HeaderStatsProps) => {
const { VEGA_EXPLORER_URL } = useEnvironment(); const { VEGA_EXPLORER_URL } = useEnvironment();
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore(); const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
const asset = market?.tradableInstrument.instrument.product?.settlementAsset; const asset = market?.tradableInstrument.instrument.product?.settlementAsset;
return ( return (
<> <div className="flex flex-col justify-end lg:pt-4">
<HeaderStat <div className="xl:flex xl:gap-4 items-end">
heading={t('Expiry')} <div
description={ data-testid="header-summary"
market && ( className="flex flex-nowrap items-end xl:flex-1 w-full overflow-x-auto text-xs"
<ExpiryTooltipContent
market={market}
explorerUrl={VEGA_EXPLORER_URL}
/>
)
}
testId="market-expiry"
>
<ExpiryLabel market={market} />
</HeaderStat>
<HeaderStat heading={t('Price')} testId="market-price">
<MarketMarkPrice
marketId={market?.id}
decimalPlaces={market?.decimalPlaces}
/>
</HeaderStat>
<HeaderStat heading={t('Change (24h)')} testId="market-change">
<Last24hPriceChange
marketId={market?.id}
decimalPlaces={market?.decimalPlaces}
/>
</HeaderStat>
<HeaderStat heading={t('Volume (24h)')} testId="market-volume">
<Last24hVolume
marketId={market?.id}
positionDecimalPlaces={market?.positionDecimalPlaces}
/>
</HeaderStat>
<HeaderStatMarketTradingMode
marketId={market?.id}
initialTradingMode={market?.tradingMode}
/>
<MarketState market={market} />
{asset ? (
<HeaderStat
heading={t('Settlement asset')}
testId="market-settlement-asset"
> >
<div> <HeaderStat
<ButtonLink heading={t('Expiry')}
onClick={(e) => { description={
openAssetDetailsDialog(asset.id, e.target as HTMLElement); market && (
}} <ExpiryTooltipContent
market={market}
explorerUrl={VEGA_EXPLORER_URL}
/>
)
}
testId="market-expiry"
>
<ExpiryLabel market={market} />
</HeaderStat>
<HeaderStat heading={t('Price')} testId="market-price">
<MarketMarkPrice
marketId={market?.id}
decimalPlaces={market?.decimalPlaces}
/>
</HeaderStat>
<HeaderStat heading={t('Change (24h)')} testId="market-change">
<Last24hPriceChange
marketId={market?.id}
decimalPlaces={market?.decimalPlaces}
/>
</HeaderStat>
<HeaderStat heading={t('Volume (24h)')} testId="market-volume">
<Last24hVolume
marketId={market?.id}
positionDecimalPlaces={market?.positionDecimalPlaces}
/>
</HeaderStat>
<HeaderStatMarketTradingMode
marketId={market?.id}
initialTradingMode={market?.tradingMode}
/>
<MarketState market={market} />
{asset ? (
<HeaderStat
heading={t('Settlement asset')}
testId="market-settlement-asset"
> >
{asset.symbol} <div>
</ButtonLink> <ButtonLink
</div> onClick={(e) => {
</HeaderStat> openAssetDetailsDialog(asset.id, e.target as HTMLElement);
) : null} }}
<MarketLiquiditySupplied >
marketId={market?.id} {asset.symbol}
assetDecimals={asset?.decimals || 0} </ButtonLink>
/> </div>
<MarketProposalNotification marketId={market?.id} /> </HeaderStat>
</> ) : null}
<MarketLiquiditySupplied
marketId={market?.id}
assetDecimals={asset?.decimals || 0}
/>
<MarketProposalNotification marketId={market?.id} />
</div>
</div>
</div>
); );
}; };
@@ -1,4 +1,5 @@
import { render, screen, waitFor } from '@testing-library/react'; import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { createMarketFragment } from '@vegaprotocol/mock'; import { createMarketFragment } from '@vegaprotocol/mock';
import { MarketSelectorItem } from './market-selector-item'; import { MarketSelectorItem } from './market-selector-item';
import { MemoryRouter } from 'react-router-dom'; import { MemoryRouter } from 'react-router-dom';
@@ -90,6 +91,8 @@ describe('MarketSelectorItem', () => {
}, },
]; ];
const mockOnSelect = jest.fn();
const renderJsx = (mocks: MockedResponse[]) => { const renderJsx = (mocks: MockedResponse[]) => {
return render( return render(
<MemoryRouter> <MemoryRouter>
@@ -98,6 +101,7 @@ describe('MarketSelectorItem', () => {
market={market} market={market}
currentMarketId={market.id} currentMarketId={market.id}
style={{}} style={{}}
onSelect={mockOnSelect}
/> />
</MockedProvider> </MockedProvider>
</MemoryRouter> </MemoryRouter>
@@ -169,7 +173,7 @@ describe('MarketSelectorItem', () => {
// link renders and is styled // link renders and is styled
expect(link).toHaveAttribute('href', '/markets/' + market.id); expect(link).toHaveAttribute('href', '/markets/' + market.id);
expect(link).toHaveClass('bg-vega-clight-600'); expect(link).toHaveClass('ring-1');
expect(screen.getByTitle('24h vol')).toHaveTextContent('0.00'); expect(screen.getByTitle('24h vol')).toHaveTextContent('0.00');
expect(screen.getByTitle(symbol)).toHaveTextContent('-'); expect(screen.getByTitle(symbol)).toHaveTextContent('-');
@@ -179,6 +183,13 @@ describe('MarketSelectorItem', () => {
expect(screen.getByTitle(symbol)).toHaveTextContent( expect(screen.getByTitle(symbol)).toHaveTextContent(
addDecimalsFormatNumber(marketData.markPrice, market.decimalPlaces) addDecimalsFormatNumber(marketData.markPrice, market.decimalPlaces)
); );
expect(screen.getByTestId('market-item-change')).toHaveTextContent(
'+100.00%'
);
}); });
await userEvent.click(link);
expect(mockOnSelect).toHaveBeenCalledWith(market.id);
}); });
}); });
@@ -0,0 +1,177 @@
import type { CSSProperties, ReactNode } from 'react';
import { Link } from 'react-router-dom';
import classNames from 'classnames';
import {
addDecimalsFormatNumber,
formatNumber,
priceChangePercentage,
} from '@vegaprotocol/utils';
import type { MarketMaybeWithDataAndCandles } from '@vegaprotocol/markets';
import { calcCandleVolume } from '@vegaprotocol/markets';
import { useCandles } from '@vegaprotocol/markets';
import { useMarketDataUpdateSubscription } from '@vegaprotocol/markets';
import { Sparkline } from '@vegaprotocol/ui-toolkit';
import {
MarketTradingMode,
MarketTradingModeMapping,
} from '@vegaprotocol/types';
import { t } from '@vegaprotocol/i18n';
export const MarketSelectorItem = ({
market,
style,
currentMarketId,
onSelect,
}: {
market: MarketMaybeWithDataAndCandles;
style: CSSProperties;
currentMarketId?: string;
onSelect?: (marketId: string) => void;
}) => {
const wrapperClasses = classNames(
'block bg-vega-light-100 dark:bg-vega-dark-100 rounded-lg p-4',
'min-h-[120px]',
{
'ring-1 ring-vega-light-300 dark:ring-vega-dark-300':
currentMarketId === market.id,
}
);
return (
<div style={style} className="my-0.5 px-4">
<Link
to={`/markets/${market.id}`}
className={wrapperClasses}
onClick={() => {
onSelect && onSelect(market.id);
}}
>
<MarketData market={market} />
</Link>
</div>
);
};
const MarketData = ({ market }: { market: MarketMaybeWithDataAndCandles }) => {
const { data } = useMarketDataUpdateSubscription({
variables: {
marketId: market.id,
},
});
const marketData = data?.marketsData[0];
// use market data price if available as this is comes from
// the subscription
const price = marketData
? addDecimalsFormatNumber(marketData.markPrice, market.decimalPlaces)
: market.data
? addDecimalsFormatNumber(market.data.markPrice, market.decimalPlaces)
: '-';
const marketTradingMode = marketData
? marketData.marketTradingMode
: market.tradingMode;
const mode = [
MarketTradingMode.TRADING_MODE_BATCH_AUCTION,
MarketTradingMode.TRADING_MODE_MONITORING_AUCTION,
MarketTradingMode.TRADING_MODE_OPENING_AUCTION,
].includes(marketTradingMode)
? MarketTradingModeMapping[marketTradingMode]
: '';
const instrument = market.tradableInstrument.instrument;
const { oneDayCandles } = useCandles({ marketId: market.id });
const vol = oneDayCandles ? calcCandleVolume(oneDayCandles) : '0';
const volume =
vol && vol !== '0'
? addDecimalsFormatNumber(vol, market.positionDecimalPlaces)
: '0.00';
return (
<>
<div className="flex items-end gap-1 mb-1">
<h3
className={classNames(
'overflow-hidden text-ellipsis whitespace-nowrap',
{
'w-1/2': mode, // make space for showing the trading mode
}
)}
>
{market.tradableInstrument.instrument.code}
</h3>
{mode && (
<p className="w-1/2 text-xs text-right text-vega-orange-500 dark:text-vega-orange-550">
{mode}
</p>
)}
</div>
<DataRow value={volume} label={t('24h vol')} />
<DataRow
value={price}
label={instrument.product.settlementAsset.symbol}
/>
<div className="relative text-xs p-1">
{oneDayCandles && (
<PriceChange candles={oneDayCandles.map((c) => c.close)} />
)}
<div
// absolute so height is not larger than price change value
className="absolute right-0 bottom-0 w-[120px]"
>
{oneDayCandles && (
<Sparkline
width={120}
height={20}
data={oneDayCandles.map((c) => Number(c.close))}
/>
)}
</div>
</div>
</>
);
};
const DataRow = ({
value,
label,
}: {
value: string | ReactNode;
label: string;
}) => {
return (
<div
className="text-ellipsis whitespace-nowrap overflow-hidden leading-tight"
data-testid="market-selector-data-row"
>
<span title={label} className="text-sm mr-1">
{value}
</span>
<span className="text-xs text-vega-light-300 dark:text-vega-light-300">
{label}
</span>
</div>
);
};
const PriceChange = ({ candles }: { candles: string[] }) => {
const priceChange = candles ? priceChangePercentage(candles) : undefined;
const priceChangeClasses = classNames('text-xs', {
'text-market-red': priceChange && priceChange < 0,
'text-market-green-600 dark:text-market-green':
priceChange && priceChange > 0,
});
let prefix = '';
if (priceChange && priceChange > 0) {
prefix = '+';
}
const formattedChange = formatNumber(Number(priceChange), 2);
return (
<div className={priceChangeClasses} data-testid="market-item-change">
{priceChange ? `${prefix}${formattedChange}%` : '-'}
</div>
);
};
@@ -143,6 +143,7 @@ describe('MarketSelector', () => {
expect(screen.getAllByTestId(/market-\d/)).toHaveLength( expect(screen.getAllByTestId(/market-\d/)).toHaveLength(
activeMarkets.length activeMarkets.length
); );
expect(screen.getByRole('link')).toHaveTextContent('All markets');
}); });
it('filters by product type', async () => { it('filters by product type', async () => {
@@ -240,7 +241,7 @@ describe('MarketSelector', () => {
await userEvent.click(screen.getByTestId('sort-trigger')); await userEvent.click(screen.getByTestId('sort-trigger'));
const options = screen.getAllByTestId(/sort-item/); const options = screen.getAllByTestId(/sort-item/);
expect(options.map((o) => o.textContent?.trim())).toEqual( expect(options.map((o) => o.textContent)).toEqual(
Object.entries(Sort) Object.entries(Sort)
.filter(([key]) => key !== Sort.None) .filter(([key]) => key !== Sort.None)
.map(([key]) => SortTypeMapping[key as SortType]) .map(([key]) => SortTypeMapping[key as SortType])
@@ -8,8 +8,10 @@ import {
VegaIconNames, VegaIconNames,
} from '@vegaprotocol/ui-toolkit'; } from '@vegaprotocol/ui-toolkit';
import type { CSSProperties } from 'react'; import type { CSSProperties } from 'react';
import { useCallback, useState, useMemo, useRef } from 'react'; import { useCallback, useState, useMemo } from 'react';
import { Link } from 'react-router-dom';
import { FixedSizeList } from 'react-window'; import { FixedSizeList } from 'react-window';
import AutoSizer from 'react-virtualized-auto-sizer';
import { useMarketSelectorList } from './use-market-selector-list'; import { useMarketSelectorList } from './use-market-selector-list';
import type { ProductType } from './product-selector'; import type { ProductType } from './product-selector';
import { Product, ProductSelector } from './product-selector'; import { Product, ProductSelector } from './product-selector';
@@ -17,7 +19,6 @@ import { AssetDropdown } from './asset-dropdown';
import type { SortType } from './sort-dropdown'; import type { SortType } from './sort-dropdown';
import { Sort, SortDropdown } from './sort-dropdown'; import { Sort, SortDropdown } from './sort-dropdown';
import { MarketSelectorItem } from './market-selector-item'; import { MarketSelectorItem } from './market-selector-item';
import classNames from 'classnames';
export type Filter = { export type Filter = {
searchTerm: string; searchTerm: string;
@@ -47,15 +48,18 @@ export const MarketSelector = ({
const { markets, data, loading, error } = useMarketSelectorList(filter); const { markets, data, loading, error } = useMarketSelectorList(filter);
return ( return (
<div data-testid="market-selector"> <div
<div className="pt-2 px-2 mb-2 w-[320px] lg:w-[584px]"> className="grid grid-rows-[min-content_1fr_min-content] h-full"
data-testid="market-selector"
>
<div className="px-4 pt-2 pb-4">
<ProductSelector <ProductSelector
product={filter.product} product={filter.product}
onSelect={(product) => { onSelect={(product) => {
setFilter((curr) => ({ ...curr, product })); setFilter((curr) => ({ ...curr, product }));
}} }}
/> />
<div className="text-sm grid grid-cols-[2fr_1fr_1fr] gap-1 "> <div className="text-sm flex gap-1 items-stretch">
<div className="flex-1"> <div className="flex-1">
<Input <Input
onChange={(e) => onChange={(e) =>
@@ -66,7 +70,20 @@ export const MarketSelector = ({
placeholder={t('Search')} placeholder={t('Search')}
data-testid="search-term" data-testid="search-term"
className="w-full" className="w-full"
prependElement={<VegaIcon name={VegaIconNames.SEARCH} />} appendElement={
filter.searchTerm.length ? (
<button
onClick={() =>
setFilter((curr) => ({ ...curr, searchTerm: '' }))
}
className="text-vega-light-200 dark:text-vega-dark-200"
>
<VegaIcon name={VegaIconNames.CROSS} />
</button>
) : (
<span />
)
}
/> />
</div> </div>
<AssetDropdown <AssetDropdown
@@ -96,6 +113,7 @@ export const MarketSelector = ({
return curr; return curr;
}); });
}} }}
onReset={() => setFilter((curr) => ({ ...curr, assets: [] }))}
/> />
<SortDropdown <SortDropdown
currentSort={filter.sort} currentSort={filter.sort}
@@ -110,6 +128,7 @@ export const MarketSelector = ({
}; };
}); });
}} }}
onReset={() => setFilter((curr) => ({ ...curr, sort: Sort.None }))}
/> />
</div> </div>
</div> </div>
@@ -130,6 +149,18 @@ export const MarketSelector = ({
} }
/> />
</div> </div>
<div className="px-4 py-2">
<span className="inline-block border-b border-black dark:border-white">
<Link
to={'/markets/all'}
data-testid="all-markets-link"
className="flex items-center gap-x-2"
>
{t('All markets')}
<VegaIcon name={VegaIconNames.ARROW_RIGHT} />
</Link>
</span>
</div>
</div> </div>
); );
}; };
@@ -150,50 +181,25 @@ const MarketList = ({
onSelect?: (marketId: string) => void; onSelect?: (marketId: string) => void;
noItems: string; noItems: string;
}) => { }) => {
const itemSize = 45;
const listRef = useRef<HTMLDivElement | null>(null);
const rect = listRef.current?.getBoundingClientRect();
// allow virtualized list to grow until it runs out of space
const height = rect
? Math.min(data.length * itemSize, window.innerHeight - rect.y)
: 400;
if (error) { if (error) {
return <div>{error.message}</div>; return <div>{error.message}</div>;
} }
return ( return (
<TinyScroll> <AutoSizer>
<div {({ width, height }) => (
className={classNames( <TinyScroll>
'flex gap-2', <List
'bg-vega-clight-700 dark:bg-vega-cdark-700', data={data}
'p-2 mx-2 border-b border-default text-xs text-secondary' loading={loading}
)} width={width}
> height={height}
<div className="w-2/5" role="columnheader"> currentMarketId={currentMarketId}
{t('Name')} onSelect={onSelect}
</div> noItems={noItems}
<div className="w-1/5" role="columnheader"> />
{t('Price')} </TinyScroll>
</div> )}
<div className="w-1/5 text-right" role="columnheader"> </AutoSizer>
{t('24h volume')}
</div>
<div className="w-1/5" role="columnheader" />
</div>
<div ref={listRef}>
<List
data={data}
loading={loading}
height={height}
itemSize={itemSize}
currentMarketId={currentMarketId}
onSelect={onSelect}
noItems={noItems}
/>
</div>
</TinyScroll>
); );
}; };
@@ -216,21 +222,22 @@ const ListItem = ({
market={data.data[index]} market={data.data[index]}
currentMarketId={data.currentMarketId} currentMarketId={data.currentMarketId}
style={style} style={style}
onSelect={data.onSelect}
/> />
); );
const List = ({ const List = ({
data, data,
loading, loading,
width,
height, height,
itemSize,
onSelect, onSelect,
noItems, noItems,
currentMarketId, currentMarketId,
}: ListItemData & { }: ListItemData & {
loading: boolean; loading: boolean;
width: number;
height: number; height: number;
itemSize: number;
noItems: string; noItems: string;
}) => { }) => {
const itemKey = useCallback( const itemKey = useCallback(
@@ -243,7 +250,7 @@ const List = ({
); );
if (!data || loading) { if (!data || loading) {
return ( return (
<div style={{ height }}> <div style={{ width, height }}>
<Skeleton /> <Skeleton />
<Skeleton /> <Skeleton />
</div> </div>
@@ -252,20 +259,24 @@ const List = ({
if (!data.length) { if (!data.length) {
return ( return (
<div style={{ height }} data-testid="no-items"> <div style={{ width, height }} data-testid="no-items">
<div className="mx-4 my-2 text-sm">{noItems}</div> <div className="mb-2 px-4">
<div className="text-sm bg-vega-light-100 dark:bg-vega-dark-100 rounded-lg px-4 py-2">
{noItems}
</div>
</div>
</div> </div>
); );
} }
return ( return (
<FixedSizeList <FixedSizeList
className="vega-scrollbar" className="virtualized-list"
itemCount={data.length} itemCount={data.length}
itemData={itemData} itemData={itemData}
itemSize={itemSize} itemSize={130}
itemKey={itemKey} itemKey={itemKey}
width="100%" width={width}
height={height} height={height}
> >
{ListItem} {ListItem}
+11 -12
View File
@@ -2,16 +2,18 @@ import React, { useEffect, useMemo } from 'react';
import { addDecimalsFormatNumber, titlefy } from '@vegaprotocol/utils'; import { addDecimalsFormatNumber, titlefy } from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import { useScreenDimensions } from '@vegaprotocol/react-helpers'; import { useScreenDimensions } from '@vegaprotocol/react-helpers';
import { useThrottledDataProvider } from '@vegaprotocol/data-provider'; import {
useDataProvider,
useThrottledDataProvider,
} from '@vegaprotocol/data-provider';
import { AsyncRenderer, ExternalLink, Splash } from '@vegaprotocol/ui-toolkit'; import { AsyncRenderer, ExternalLink, Splash } from '@vegaprotocol/ui-toolkit';
import { marketDataProvider, useMarket } from '@vegaprotocol/markets'; import { marketProvider, marketDataProvider } from '@vegaprotocol/markets';
import { useGlobalStore, usePageTitleStore } from '../../stores'; import { useGlobalStore, usePageTitleStore } from '../../stores';
import { TradeGrid } from './trade-grid'; import { TradeGrid } from './trade-grid';
import { TradePanels } from './trade-panels'; import { TradePanels } from './trade-panels';
import { useNavigate, useParams } from 'react-router-dom'; import { useNavigate, useParams } from 'react-router-dom';
import { Links, Routes } from '../../pages/client-router'; import { Links, Routes } from '../../pages/client-router';
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler'; import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
import { ViewType, useSidebar } from '../../components/sidebar';
const calculatePrice = (markPrice?: string, decimalPlaces?: number) => { const calculatePrice = (markPrice?: string, decimalPlaces?: number) => {
return markPrice && decimalPlaces return markPrice && decimalPlaces
@@ -59,7 +61,7 @@ const TitleUpdater = ({
export const MarketPage = () => { export const MarketPage = () => {
const { marketId } = useParams(); const { marketId } = useParams();
const navigate = useNavigate(); const navigate = useNavigate();
const { init, view, setView } = useSidebar();
const { screenSize } = useScreenDimensions(); const { screenSize } = useScreenDimensions();
const largeScreen = ['lg', 'xl', 'xxl', 'xxxl'].includes(screenSize); const largeScreen = ['lg', 'xl', 'xxl', 'xxxl'].includes(screenSize);
const update = useGlobalStore((store) => store.update); const update = useGlobalStore((store) => store.update);
@@ -67,7 +69,11 @@ export const MarketPage = () => {
const onSelect = useMarketClickHandler(); const onSelect = useMarketClickHandler();
const { data, error, loading } = useMarket(marketId); const { data, error, loading } = useDataProvider({
dataProvider: marketProvider,
variables: { marketId: marketId || '' },
skip: !marketId,
});
useEffect(() => { useEffect(() => {
if (data?.id && data.id !== lastMarketId) { if (data?.id && data.id !== lastMarketId) {
@@ -75,13 +81,6 @@ export const MarketPage = () => {
} }
}, [update, lastMarketId, data?.id]); }, [update, lastMarketId, data?.id]);
// Make sidebar open on deal ticket by default
useEffect(() => {
if (init && view === null) {
setView({ type: ViewType.Order });
}
}, [init, view, setView]);
const tradeView = useMemo(() => { const tradeView = useMemo(() => {
if (largeScreen) { if (largeScreen) {
return ( return (
@@ -1,8 +1,4 @@
import classNames from 'classnames'; import classNames from 'classnames';
import { Link } from 'react-router-dom';
import { Routes } from '../../pages/client-router';
import { t } from '@vegaprotocol/i18n';
import { VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
// Make sure these match the available __typename properties on product // Make sure these match the available __typename properties on product
export const Product = { export const Product = {
@@ -29,12 +25,12 @@ export const ProductSelector = ({
onSelect: (product: ProductType) => void; onSelect: (product: ProductType) => void;
}) => { }) => {
return ( return (
<div className="flex mb-2"> <div className="flex gap-3 mb-3">
{Object.keys(Product).map((t) => { {Object.keys(Product).map((t) => {
const classes = classNames('px-3 py-1.5 rounded', { const classes = classNames('py-1 border-b-2', {
'bg-vega-clight-500 dark:bg-vega-cdark-500 text-default': 'border-vega-yellow text-black dark:text-white': t === product,
t === product, 'border-transparent text-vega-light-300 dark:text-vega-dark-300':
'text-secondary': t !== product, t !== product,
}); });
return ( return (
<button <button
@@ -49,10 +45,6 @@ export const ProductSelector = ({
</button> </button>
); );
})} })}
<Link to={Routes.MARKETS} className="flex items-center gap-2 ml-auto">
<span className="underline underline-offset-4">{t('All markets')}</span>
<VegaIcon name={VegaIconNames.ARROW_RIGHT} />
</Link>
</div> </div>
); );
}; };
@@ -2,10 +2,12 @@ import { t } from '@vegaprotocol/i18n';
import { import {
DropdownMenu, DropdownMenu,
DropdownMenuContent, DropdownMenuContent,
DropdownMenuItem,
DropdownMenuItemIndicator, DropdownMenuItemIndicator,
DropdownMenuRadioGroup, DropdownMenuRadioGroup,
DropdownMenuRadioItem, DropdownMenuRadioItem,
DropdownMenuTrigger, DropdownMenuTrigger,
DropdownMenuSeparator,
VegaIcon, VegaIcon,
VegaIconNames, VegaIconNames,
} from '@vegaprotocol/ui-toolkit'; } from '@vegaprotocol/ui-toolkit';
@@ -28,32 +30,20 @@ export const SortTypeMapping: {
[Sort.New]: 'New markets', [Sort.New]: 'New markets',
}; };
const SortIconMapping: {
[key in SortType]: VegaIconNames;
} = {
[Sort.None]: null as unknown as VegaIconNames, // not shown in list
[Sort.Gained]: VegaIconNames.TREND_UP,
[Sort.Lost]: VegaIconNames.TREND_DOWN,
[Sort.New]: VegaIconNames.STAR,
};
export const SortDropdown = ({ export const SortDropdown = ({
currentSort, currentSort,
onSelect, onSelect,
onReset,
}: { }: {
currentSort: SortType; currentSort: SortType;
onSelect: (sort: SortType) => void; onSelect: (sort: SortType) => void;
onReset: () => void;
}) => { }) => {
return ( return (
<DropdownMenu <DropdownMenu
trigger={ trigger={
<DropdownMenuTrigger data-testid="sort-trigger"> <DropdownMenuTrigger data-testid="sort-trigger">
<span className="flex justify-between items-center"> <VegaIcon name={VegaIconNames.TREND_UP} />
{currentSort === SortTypeMapping.None
? t('Sort')
: SortTypeMapping[currentSort]}{' '}
<VegaIcon name={VegaIconNames.CHEVRON_DOWN} />
</span>
</DropdownMenuTrigger> </DropdownMenuTrigger>
} }
> >
@@ -62,6 +52,8 @@ export const SortDropdown = ({
value={currentSort} value={currentSort}
onValueChange={(value) => onSelect(value as SortType)} onValueChange={(value) => onSelect(value as SortType)}
> >
<DropdownMenuItem onClick={onReset}>{t('Reset')}</DropdownMenuItem>
<DropdownMenuSeparator />
{Object.keys(Sort) {Object.keys(Sort)
.filter((s) => s !== Sort.None) .filter((s) => s !== Sort.None)
.map((key) => { .map((key) => {
@@ -72,10 +64,7 @@ export const SortDropdown = ({
value={key} value={key}
data-testid={`sort-item-${key}`} data-testid={`sort-item-${key}`}
> >
<span className="flex gap-2"> {SortTypeMapping[key as SortType]}
<VegaIcon name={SortIconMapping[key as SortType]} />{' '}
{SortTypeMapping[key as SortType]}
</span>
<DropdownMenuItemIndicator /> <DropdownMenuItemIndicator />
</DropdownMenuRadioItem> </DropdownMenuRadioItem>
); );
+229 -73
View File
@@ -1,5 +1,6 @@
import { memo } from 'react'; import { memo, useState } from 'react';
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
import { useNavigate } from 'react-router-dom';
import { LayoutPriority } from 'allotment'; import { LayoutPriority } from 'allotment';
import classNames from 'classnames'; import classNames from 'classnames';
import AutoSizer from 'react-virtualized-auto-sizer'; import AutoSizer from 'react-virtualized-auto-sizer';
@@ -8,22 +9,177 @@ import { t } from '@vegaprotocol/i18n';
import { OracleBanner } from '@vegaprotocol/markets'; import { OracleBanner } from '@vegaprotocol/markets';
import type { Market } from '@vegaprotocol/markets'; import type { Market } from '@vegaprotocol/markets';
import { Filter } from '@vegaprotocol/orders'; import { Filter } from '@vegaprotocol/orders';
import { Tab, LocalStoragePersistTabs as Tabs } from '@vegaprotocol/ui-toolkit'; import { useScreenDimensions } from '@vegaprotocol/react-helpers';
import {
Tab,
LocalStoragePersistTabs as Tabs,
VegaIcon,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler'; import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
import { VegaWalletContainer } from '../../components/vega-wallet-container'; import { VegaWalletContainer } from '../../components/vega-wallet-container';
import { HeaderTitle } from '../../components/header';
import { import {
ResizableGrid, ResizableGrid,
ResizableGridPanel, ResizableGridPanel,
usePaneLayout, usePaneLayout,
} from '../../components/resizable-grid'; } from '../../components/resizable-grid';
import { TradingViews } from './trade-views'; import { TradingViews } from './trade-views';
import { MarketSuccessorBanner } from '../../components/market-banner'; import { MarketSelector } from './market-selector';
import { HeaderStats } from './header-stats';
interface TradeGridProps { interface TradeGridProps {
market: Market | null; market: Market | null;
onSelect: (marketId: string, metaKey?: boolean) => void; onSelect: (marketId: string, metaKey?: boolean) => void;
pinnedAsset?: PinnedAsset; pinnedAsset?: PinnedAsset;
} }
interface BottomPanelProps {
marketId: string;
pinnedAsset?: PinnedAsset;
}
const MarketBottomPanel = memo(
({ marketId, pinnedAsset }: BottomPanelProps) => {
const [sizes, handleOnLayoutChange] = usePaneLayout({ id: 'bottom' });
const { screenSize } = useScreenDimensions();
const onMarketClick = useMarketClickHandler(true);
return 'xxxl' === screenSize ? (
<ResizableGrid
proportionalLayout
minSize={200}
onChange={handleOnLayoutChange}
>
<ResizableGridPanel
priority={LayoutPriority.Low}
preferredSize={sizes[0] || '50%'}
minSize={50}
>
<TradeGridChild>
<Tabs storageKey="console-trade-grid-bottom-left">
<Tab id="open-orders" name={t('Open')}>
<VegaWalletContainer>
<TradingViews.orders.component
marketId={marketId}
filter={Filter.Open}
/>
</VegaWalletContainer>
</Tab>
<Tab id="closed-orders" name={t('Closed')}>
<VegaWalletContainer>
<TradingViews.orders.component
marketId={marketId}
filter={Filter.Closed}
/>
</VegaWalletContainer>
</Tab>
<Tab id="rejected-orders" name={t('Rejected')}>
<VegaWalletContainer>
<TradingViews.orders.component
marketId={marketId}
filter={Filter.Rejected}
/>
</VegaWalletContainer>
</Tab>
<Tab id="orders" name={t('All')}>
<VegaWalletContainer>
<TradingViews.orders.component marketId={marketId} />
</VegaWalletContainer>
</Tab>
<Tab id="fills" name={t('Fills')}>
<VegaWalletContainer>
<TradingViews.fills.component onMarketClick={onMarketClick} />
</VegaWalletContainer>
</Tab>
</Tabs>
</TradeGridChild>
</ResizableGridPanel>
<ResizableGridPanel
priority={LayoutPriority.Low}
preferredSize={sizes[1] || '50%'}
minSize={50}
>
<TradeGridChild>
<Tabs storageKey="console-trade-grid-bottom-right">
<Tab id="positions" name={t('Positions')}>
<VegaWalletContainer>
<TradingViews.positions.component
onMarketClick={onMarketClick}
/>
</VegaWalletContainer>
</Tab>
<Tab id="accounts" name={t('Collateral')}>
<VegaWalletContainer>
<TradingViews.collateral.component
pinnedAsset={pinnedAsset}
onMarketClick={onMarketClick}
hideButtons
/>
</VegaWalletContainer>
</Tab>
</Tabs>
</TradeGridChild>
</ResizableGridPanel>
</ResizableGrid>
) : (
<TradeGridChild>
<Tabs storageKey="console-trade-grid-bottom">
<Tab id="positions" name={t('Positions')}>
<VegaWalletContainer>
<TradingViews.positions.component onMarketClick={onMarketClick} />
</VegaWalletContainer>
</Tab>
<Tab id="open-orders" name={t('Open')}>
<VegaWalletContainer>
<TradingViews.orders.component
marketId={marketId}
filter={Filter.Open}
/>
</VegaWalletContainer>
</Tab>
<Tab id="closed-orders" name={t('Closed')}>
<VegaWalletContainer>
<TradingViews.orders.component
marketId={marketId}
filter={Filter.Closed}
/>
</VegaWalletContainer>
</Tab>
<Tab id="rejected-orders" name={t('Rejected')}>
<VegaWalletContainer>
<TradingViews.orders.component
marketId={marketId}
filter={Filter.Rejected}
/>
</VegaWalletContainer>
</Tab>
<Tab id="orders" name={t('All')}>
<VegaWalletContainer>
<TradingViews.orders.component marketId={marketId} />
</VegaWalletContainer>
</Tab>
<Tab id="fills" name={t('Fills')}>
<VegaWalletContainer>
<TradingViews.fills.component onMarketClick={onMarketClick} />
</VegaWalletContainer>
</Tab>
<Tab id="accounts" name={t('Collateral')}>
<VegaWalletContainer>
<TradingViews.collateral.component
pinnedAsset={pinnedAsset}
onMarketClick={onMarketClick}
hideButtons
/>
</VegaWalletContainer>
</Tab>
</Tabs>
</TradeGridChild>
);
}
);
MarketBottomPanel.displayName = 'MarketBottomPanel';
const MainGrid = memo( const MainGrid = memo(
({ ({
marketId, marketId,
@@ -32,6 +188,7 @@ const MainGrid = memo(
marketId: string; marketId: string;
pinnedAsset?: PinnedAsset; pinnedAsset?: PinnedAsset;
}) => { }) => {
const navigate = useNavigate();
const [sizes, handleOnLayoutChange] = usePaneLayout({ id: 'top' }); const [sizes, handleOnLayoutChange] = usePaneLayout({ id: 'top' });
const [sizesMiddle, handleOnMiddleLayoutChange] = usePaneLayout({ const [sizesMiddle, handleOnMiddleLayoutChange] = usePaneLayout({
id: 'middle-1', id: 'middle-1',
@@ -46,6 +203,25 @@ const MainGrid = memo(
minSize={200} minSize={200}
onChange={handleOnMiddleLayoutChange} onChange={handleOnMiddleLayoutChange}
> >
<ResizableGridPanel
preferredSize={sizesMiddle[0] || 330}
minSize={300}
>
<TradeGridChild>
<Tabs storageKey="console-trade-grid-main-center">
<Tab id="ticket" name={t('Ticket')}>
<TradingViews.ticket.component
marketId={marketId}
onMarketClick={onMarketClick}
onClickCollateral={() => navigate('/portfolio')}
/>
</Tab>
<Tab id="info" name={t('Info')}>
<TradingViews.info.component marketId={marketId} />
</Tab>
</Tabs>
</TradeGridChild>
</ResizableGridPanel>
<ResizableGridPanel <ResizableGridPanel
priority={LayoutPriority.High} priority={LayoutPriority.High}
minSize={200} minSize={200}
@@ -87,63 +263,7 @@ const MainGrid = memo(
preferredSize={sizes[1] || '25%'} preferredSize={sizes[1] || '25%'}
minSize={50} minSize={50}
> >
<TradeGridChild> <MarketBottomPanel marketId={marketId} pinnedAsset={pinnedAsset} />
<Tabs storageKey="console-trade-grid-bottom">
<Tab id="positions" name={t('Positions')}>
<VegaWalletContainer>
<TradingViews.positions.component
onMarketClick={onMarketClick}
/>
</VegaWalletContainer>
</Tab>
<Tab id="open-orders" name={t('Open')}>
<VegaWalletContainer>
<TradingViews.orders.component
marketId={marketId}
filter={Filter.Open}
/>
</VegaWalletContainer>
</Tab>
<Tab id="closed-orders" name={t('Closed')}>
<VegaWalletContainer>
<TradingViews.orders.component
marketId={marketId}
filter={Filter.Closed}
/>
</VegaWalletContainer>
</Tab>
<Tab id="rejected-orders" name={t('Rejected')}>
<VegaWalletContainer>
<TradingViews.orders.component
marketId={marketId}
filter={Filter.Rejected}
/>
</VegaWalletContainer>
</Tab>
<Tab id="orders" name={t('All')}>
<VegaWalletContainer>
<TradingViews.orders.component marketId={marketId} />
</VegaWalletContainer>
</Tab>
<Tab id="fills" name={t('Fills')}>
<VegaWalletContainer>
<TradingViews.fills.component
marketId={marketId}
onMarketClick={onMarketClick}
/>
</VegaWalletContainer>
</Tab>
<Tab id="accounts" name={t('Collateral')}>
<VegaWalletContainer>
<TradingViews.collateral.component
pinnedAsset={pinnedAsset}
onMarketClick={onMarketClick}
hideButtons
/>
</VegaWalletContainer>
</Tab>
</Tabs>
</TradeGridChild>
</ResizableGridPanel> </ResizableGridPanel>
</ResizableGrid> </ResizableGrid>
); );
@@ -152,18 +272,61 @@ const MainGrid = memo(
MainGrid.displayName = 'MainGrid'; MainGrid.displayName = 'MainGrid';
export const TradeGrid = ({ market, pinnedAsset }: TradeGridProps) => { export const TradeGrid = ({ market, pinnedAsset }: TradeGridProps) => {
const [sidebarOpen, setSidebarOpen] = useState(true);
const wrapperClasses = classNames( const wrapperClasses = classNames(
'h-full grid', 'h-full grid',
'grid-rows-[min-content_1fr]' 'grid-rows-[min-content_min-content_1fr]',
'grid-cols-[320px_1fr]'
); );
const paneWrapperClasses = classNames('min-h-0', {
'col-span-2 col-start-1': !sidebarOpen,
});
return ( return (
<div className={wrapperClasses}> <div className={wrapperClasses}>
<div> <div className="border-b border-r border-default">
<MarketSuccessorBanner market={market} /> <div className="h-full flex gap-2 justify-between items-end px-4 pt-1 pb-3">
<HeaderTitle
primaryContent={market?.tradableInstrument.instrument.code}
secondaryContent={market?.tradableInstrument.instrument.name}
/>
<button
onClick={() => setSidebarOpen((x) => !x)}
className="flex flex-col items-center text-xs w-12"
data-testid="sidebar-toggle"
>
{sidebarOpen ? (
<>
<VegaIcon name={VegaIconNames.CHEVRON_UP} />
<span className="text-vega-light-300 dark:text-vega-dark-300">
{t('Close')}
</span>
</>
) : (
<>
<VegaIcon name={VegaIconNames.CHEVRON_DOWN} />
<span className="text-vega-light-300 dark:text-vega-dark-300">
{t('Markets')}
</span>
</>
)}
</button>
</div>
</div>
<div className="border-b border-default min-w-0">
<HeaderStats market={market} />
</div>
<div className="col-span-2">
<OracleBanner marketId={market?.id || ''} /> <OracleBanner marketId={market?.id || ''} />
</div> </div>
<div className="min-h-0 p-0.5"> {sidebarOpen && (
<div className="border-r border-default min-h-0">
<div className="h-full pb-8">
<MarketSelector currentMarketId={market?.id} />
</div>
</div>
)}
<div className={paneWrapperClasses}>
<MainGrid marketId={market?.id || ''} pinnedAsset={pinnedAsset} /> <MainGrid marketId={market?.id || ''} pinnedAsset={pinnedAsset} />
</div> </div>
</div> </div>
@@ -176,16 +339,9 @@ interface TradeGridChildProps {
const TradeGridChild = ({ children }: TradeGridChildProps) => { const TradeGridChild = ({ children }: TradeGridChildProps) => {
return ( return (
<section className="h-full p-1"> <section className="h-full">
<AutoSizer> <AutoSizer>
{({ width, height }) => ( {({ width, height }) => <div style={{ width, height }}>{children}</div>}
<div
style={{ width, height }}
className="border border-default rounded-sm"
>
{children}
</div>
)}
</AutoSizer> </AutoSizer>
</section> </section>
); );
@@ -8,11 +8,19 @@ import {
import type { TradingView } from './trade-views'; import type { TradingView } from './trade-views';
import { TradingViews } from './trade-views'; import { TradingViews } from './trade-views';
import { memo, useState } from 'react'; import { memo, useState } from 'react';
import { Splash } from '@vegaprotocol/ui-toolkit'; import {
Icon,
Splash,
VegaIcon,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import { NO_MARKET } from './constants'; import { NO_MARKET } from './constants';
import AutoSizer from 'react-virtualized-auto-sizer'; import AutoSizer from 'react-virtualized-auto-sizer';
import classNames from 'classnames'; import classNames from 'classnames';
import { MarketSuccessorBanner } from '../../components/market-banner'; import { HeaderStats } from './header-stats';
import * as DialogPrimitives from '@radix-ui/react-dialog';
import { HeaderTitle } from '../../components/header';
import { MarketSelector } from './market-selector';
interface TradePanelsProps { interface TradePanelsProps {
market: Market | null; market: Market | null;
@@ -29,6 +37,7 @@ export const TradePanels = ({
onClickCollateral, onClickCollateral,
pinnedAsset, pinnedAsset,
}: TradePanelsProps) => { }: TradePanelsProps) => {
const [drawerOpen, setDrawerOpen] = useState(false);
const onMarketClick = useMarketClickHandler(true); const onMarketClick = useMarketClickHandler(true);
const onOrderTypeClick = useMarketLiquidityClickHandler(); const onOrderTypeClick = useMarketLiquidityClickHandler();
@@ -62,9 +71,27 @@ export const TradePanels = ({
}; };
return ( return (
<div className="h-full grid grid-rows-[min-content_1fr_min-content]"> <div className="h-full grid grid-rows-[min-content_min-content_1fr_min-content]">
<div className="border-b border-default min-w-0">
<div className="flex gap-4 items-center px-4 py-2">
<HeaderTitle
primaryContent={market?.tradableInstrument.instrument.code}
secondaryContent={market?.tradableInstrument.instrument.name}
/>
<button onClick={() => setDrawerOpen((x) => !x)} className="p-2">
<span
className={classNames('block', {
'rotate-90 translate-x-1': !drawerOpen,
'-rotate-90 -translate-x-1': drawerOpen,
})}
>
<VegaIcon name={VegaIconNames.CHEVRON_UP} />
</span>
</button>
</div>
<HeaderStats market={market} />
</div>
<div> <div>
<MarketSuccessorBanner market={market} />
<OracleBanner marketId={market?.id || ''} /> <OracleBanner marketId={market?.id || ''} />
</div> </div>
<div className="h-full"> <div className="h-full">
@@ -80,7 +107,8 @@ export const TradePanels = ({
{Object.keys(TradingViews).map((key) => { {Object.keys(TradingViews).map((key) => {
const isActive = view === key; const isActive = view === key;
const className = classNames('p-4 min-w-[100px] capitalize', { const className = classNames('p-4 min-w-[100px] capitalize', {
'bg-vega-clight-500 dark:bg-vega-cdark-500': isActive, 'text-black dark:text-vega-yellow': isActive,
'bg-neutral-200 dark:bg-neutral-800': isActive,
}); });
return ( return (
<button <button
@@ -94,6 +122,25 @@ export const TradePanels = ({
); );
})} })}
</div> </div>
<DialogPrimitives.Root open={drawerOpen} onOpenChange={setDrawerOpen}>
<DialogPrimitives.Portal>
<DialogPrimitives.Overlay />
<DialogPrimitives.Content
className={classNames(
'fixed h-full max-w-[500px] w-[90vw] z-10 top-0 left-0 transition-transform',
'bg-white dark:bg-black',
'border-r border-default'
)}
>
<DialogPrimitives.Close className="absolute top-0 right-0 p-2">
<Icon name="cross" />
</DialogPrimitives.Close>
{drawerOpen && (
<MarketSelector onSelect={() => setDrawerOpen(false)} />
)}
</DialogPrimitives.Content>
</DialogPrimitives.Portal>
</DialogPrimitives.Root>
</div> </div>
); );
}; };
@@ -1,11 +1,13 @@
import type { ComponentProps } from 'react'; import type { ComponentProps } from 'react';
import { Splash } from '@vegaprotocol/ui-toolkit'; import { Splash } from '@vegaprotocol/ui-toolkit';
import { DealTicketContainer } from '@vegaprotocol/deal-ticket';
import { MarketInfoAccordionContainer } from '@vegaprotocol/markets';
import { TradesContainer } from '@vegaprotocol/trades'; import { TradesContainer } from '@vegaprotocol/trades';
import { DepthChartContainer } from '@vegaprotocol/market-depth'; import { DepthChartContainer } from '@vegaprotocol/market-depth';
import { CandlesChartContainer } from '@vegaprotocol/candles-chart'; import { CandlesChartContainer } from '@vegaprotocol/candles-chart';
import { OrderbookContainer } from '@vegaprotocol/market-depth';
import { Filter } from '@vegaprotocol/orders'; import { Filter } from '@vegaprotocol/orders';
import { NO_MARKET } from './constants'; import { NO_MARKET } from './constants';
import { OrderbookContainer } from '../../components/orderbook-container';
import { FillsContainer } from '../../components/fills-container'; import { FillsContainer } from '../../components/fills-container';
import { PositionsContainer } from '../../components/positions-container'; import { PositionsContainer } from '../../components/positions-container';
import { AccountsContainer } from '../../components/accounts-container'; import { AccountsContainer } from '../../components/accounts-container';
@@ -16,6 +18,8 @@ import { OrdersContainer } from '../../components/orders-container';
type MarketDependantView = type MarketDependantView =
| typeof CandlesChartContainer | typeof CandlesChartContainer
| typeof DepthChartContainer | typeof DepthChartContainer
| typeof DealTicketContainer
| typeof MarketInfoAccordionContainer
| typeof OrderbookContainer | typeof OrderbookContainer
| typeof TradesContainer; | typeof TradesContainer;
@@ -43,6 +47,14 @@ export const TradingViews = {
label: 'Liquidity', label: 'Liquidity',
component: requiresMarket(LiquidityContainer), component: requiresMarket(LiquidityContainer),
}, },
ticket: {
label: 'Ticket',
component: requiresMarket(DealTicketContainer),
},
info: {
label: 'Info',
component: requiresMarket(MarketInfoAccordionContainer),
},
orderbook: { orderbook: {
label: 'Orderbook', label: 'Orderbook',
component: requiresMarket(OrderbookContainer), component: requiresMarket(OrderbookContainer),
@@ -4,12 +4,12 @@ import {
isMarketActive, isMarketActive,
useMarketSelectorList, useMarketSelectorList,
} from './use-market-selector-list'; } from './use-market-selector-list';
import { Product } from '../../components/market-selector/product-selector'; import { Product } from './product-selector';
import { Sort } from './sort-dropdown'; import { Sort } from './sort-dropdown';
import { createMarketFragment } from '@vegaprotocol/mock'; import { createMarketFragment } from '@vegaprotocol/mock';
import { MarketState } from '@vegaprotocol/types'; import { MarketState } from '@vegaprotocol/types';
import { useMarketList } from '@vegaprotocol/markets'; import { useMarketList } from '@vegaprotocol/markets';
import type { Filter } from '../../components/market-selector'; import type { Filter } from './market-selector';
import { subDays } from 'date-fns'; import { subDays } from 'date-fns';
jest.mock('@vegaprotocol/markets'); jest.mock('@vegaprotocol/markets');
@@ -3,7 +3,7 @@ import orderBy from 'lodash/orderBy';
import { MarketState } from '@vegaprotocol/types'; import { MarketState } from '@vegaprotocol/types';
import { calcCandleVolume, useMarketList } from '@vegaprotocol/markets'; import { calcCandleVolume, useMarketList } from '@vegaprotocol/markets';
import { priceChangePercentage } from '@vegaprotocol/utils'; import { priceChangePercentage } from '@vegaprotocol/utils';
import type { Filter } from '../../components/market-selector/market-selector'; import type { Filter } from './market-selector';
import { Sort } from './sort-dropdown'; import { Sort } from './sort-dropdown';
// Used for sort order and filter // Used for sort order and filter
@@ -1,7 +1,7 @@
import { act, render, screen, within, waitFor } from '@testing-library/react'; import { act, render, screen, within } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import { Closed } from './closed'; import { Closed } from './closed';
import { MarketStateMapping, PropertyKeyType } from '@vegaprotocol/types'; import { MarketStateMapping, PropertyKeyType } from '@vegaprotocol/types';
import { PositionStatus } from '@vegaprotocol/types';
import { MarketState } from '@vegaprotocol/types'; import { MarketState } from '@vegaprotocol/types';
import { subDays } from 'date-fns'; import { subDays } from 'date-fns';
import type { MockedResponse } from '@apollo/client/testing'; import type { MockedResponse } from '@apollo/client/testing';
@@ -18,6 +18,11 @@ import {
} from '@vegaprotocol/markets'; } from '@vegaprotocol/markets';
import type { VegaWalletContextShape } from '@vegaprotocol/wallet'; import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
import { VegaWalletContext } from '@vegaprotocol/wallet'; import { VegaWalletContext } from '@vegaprotocol/wallet';
import type {
PositionsQuery,
PositionFieldsFragment,
} from '@vegaprotocol/positions';
import { PositionsDocument } from '@vegaprotocol/positions';
import { addDecimalsFormatNumber } from '@vegaprotocol/utils'; import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
import { import {
createMarketFragment, createMarketFragment,
@@ -155,6 +160,45 @@ describe('Closed', () => {
}, },
}; };
// Create mock position
const createPosition = (): PositionFieldsFragment => {
return {
__typename: 'Position' as const,
realisedPNL: '1000',
unrealisedPNL: '2000',
openVolume: '3000',
averageEntryPrice: '100',
updatedAt: new Date().toISOString(),
positionStatus: PositionStatus.POSITION_STATUS_UNSPECIFIED,
lossSocializationAmount: '1000',
market: {
__typename: 'Market',
id: marketId,
},
party: {
__typename: 'Party',
id: pubKey,
},
};
};
const position = createPosition();
const positionsMock: MockedResponse<PositionsQuery> = {
request: {
query: PositionsDocument,
variables: {
partyIds: [pubKey],
},
},
result: {
data: {
positions: {
__typename: 'PositionConnection',
edges: [{ __typename: 'PositionEdge', node: position }],
},
},
},
};
beforeAll(() => { beforeAll(() => {
originalNow = Date.now; originalNow = Date.now;
Date.now = jest.fn().mockReturnValue(mockNowTimestamp); Date.now = jest.fn().mockReturnValue(mockNowTimestamp);
@@ -167,17 +211,15 @@ describe('Closed', () => {
it('renders correctly formatted and filtered rows', async () => { it('renders correctly formatted and filtered rows', async () => {
await act(async () => { await act(async () => {
render( render(
<MemoryRouter> <MockedProvider
<MockedProvider mocks={[marketsMock, marketsDataMock, positionsMock, oracleDataMock]}
mocks={[marketsMock, marketsDataMock, oracleDataMock]} >
<VegaWalletContext.Provider
value={{ pubKey } as VegaWalletContextShape}
> >
<VegaWalletContext.Provider <Closed />
value={{ pubKey } as VegaWalletContextShape} </VegaWalletContext.Provider>
> </MockedProvider>
<Closed />
</VegaWalletContext.Provider>
</MockedProvider>
</MemoryRouter>
); );
}); });
// screen.debug(document, Infinity); // screen.debug(document, Infinity);
@@ -188,11 +230,11 @@ describe('Closed', () => {
'Description', 'Description',
'Status', 'Status',
'Settlement date', 'Settlement date',
'Successor market',
'Best bid', 'Best bid',
'Best offer', 'Best offer',
'Mark price', 'Mark price',
'Settlement price', 'Settlement price',
'Realised PNL',
'Settlement asset', 'Settlement asset',
'', // actions row '', // actions row
]; ];
@@ -205,7 +247,6 @@ describe('Closed', () => {
market.tradableInstrument.instrument.name, market.tradableInstrument.instrument.name,
MarketStateMapping[market.state], MarketStateMapping[market.state],
'3 days ago', '3 days ago',
'-',
/* eslint-disable @typescript-eslint/no-non-null-assertion */ /* eslint-disable @typescript-eslint/no-non-null-assertion */
addDecimalsFormatNumber(marketsData.bestBidPrice, market.decimalPlaces), addDecimalsFormatNumber(marketsData.bestBidPrice, market.decimalPlaces),
addDecimalsFormatNumber( addDecimalsFormatNumber(
@@ -215,6 +256,7 @@ describe('Closed', () => {
addDecimalsFormatNumber(marketsData!.markPrice, market.decimalPlaces), addDecimalsFormatNumber(marketsData!.markPrice, market.decimalPlaces),
/* eslint-enable @typescript-eslint/no-non-null-assertion */ /* eslint-enable @typescript-eslint/no-non-null-assertion */
addDecimalsFormatNumber(property.value, market.decimalPlaces), addDecimalsFormatNumber(property.value, market.decimalPlaces),
addDecimalsFormatNumber(position.realisedPNL, market.decimalPlaces),
market.tradableInstrument.instrument.product.settlementAsset.symbol, market.tradableInstrument.instrument.product.settlementAsset.symbol,
'', // actions row '', // actions row
]; ];
@@ -273,17 +315,20 @@ describe('Closed', () => {
}; };
await act(async () => { await act(async () => {
render( render(
<MemoryRouter> <MockedProvider
<MockedProvider mocks={[
mocks={[mixedMarketsMock, marketsDataMock, oracleDataMock]} mixedMarketsMock,
marketsDataMock,
positionsMock,
oracleDataMock,
]}
>
<VegaWalletContext.Provider
value={{ pubKey } as VegaWalletContextShape}
> >
<VegaWalletContext.Provider <Closed />
value={{ pubKey } as VegaWalletContextShape} </VegaWalletContext.Provider>
> </MockedProvider>
<Closed />
</VegaWalletContext.Provider>
</MockedProvider>
</MemoryRouter>
); );
}); });
@@ -314,69 +359,4 @@ describe('Closed', () => {
}); });
expect(cells).toEqual(expectedRows.map((m) => m.node.id)); expect(cells).toEqual(expectedRows.map((m) => m.node.id));
}); });
it('successor marked should be visible', async () => {
const mixedMarkets = [
{
__typename: 'MarketEdge' as const,
node: createMarketFragment({
id: 'include-0',
state: MarketState.STATE_SETTLED,
successorMarketID: 'successorMarketID',
}),
},
{
__typename: 'MarketEdge' as const,
node: {
...createMarketFragment({
id: 'successorMarketID',
state: MarketState.STATE_ACTIVE,
}),
tradableInstrument: {
...createMarketFragment().tradableInstrument,
instrument: {
...createMarketFragment().tradableInstrument.instrument,
id: 'successorAssset',
name: 'Successor Market Name',
code: 'SuccessorCode',
},
},
},
},
];
const mixedMarketsMock: MockedResponse<MarketsQuery> = {
request: {
query: MarketsDocument,
},
result: {
data: {
marketsConnection: {
__typename: 'MarketConnection',
edges: mixedMarkets,
},
},
},
};
render(
<MemoryRouter>
<MockedProvider
mocks={[mixedMarketsMock, marketsDataMock, oracleDataMock]}
>
<VegaWalletContext.Provider
value={{ pubKey } as VegaWalletContextShape}
>
<Closed />
</VegaWalletContext.Provider>
</MockedProvider>
</MemoryRouter>
);
await waitFor(() => {
expect(
screen.getByRole('button', { name: 'SuccessorCode' })
).toBeInTheDocument();
});
});
}); });
+38 -40
View File
@@ -4,11 +4,7 @@ import type {
VegaICellRendererParams, VegaICellRendererParams,
VegaValueFormatterParams, VegaValueFormatterParams,
} from '@vegaprotocol/datagrid'; } from '@vegaprotocol/datagrid';
import { import { AgGridLazy as AgGrid, COL_DEFS } from '@vegaprotocol/datagrid';
AgGridLazy as AgGrid,
COL_DEFS,
MarketNameCell,
} from '@vegaprotocol/datagrid';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import { MarketState, MarketStateMapping } from '@vegaprotocol/types'; import { MarketState, MarketStateMapping } from '@vegaprotocol/types';
@@ -16,6 +12,7 @@ import {
addDecimalsFormatNumber, addDecimalsFormatNumber,
getMarketExpiryDate, getMarketExpiryDate,
} from '@vegaprotocol/utils'; } from '@vegaprotocol/utils';
import { usePositionsQuery } from '@vegaprotocol/positions';
import type { import type {
DataSourceFilterFragment, DataSourceFilterFragment,
MarketMaybeWithData, MarketMaybeWithData,
@@ -23,14 +20,13 @@ import type {
import { import {
MarketActionsDropdown, MarketActionsDropdown,
closedMarketsWithDataProvider, closedMarketsWithDataProvider,
marketProvider,
} from '@vegaprotocol/markets'; } from '@vegaprotocol/markets';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets'; import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
import type { ColDef } from 'ag-grid-community'; import type { ColDef } from 'ag-grid-community';
import { SettlementDateCell } from './settlement-date-cell'; import { SettlementDateCell } from './settlement-date-cell';
import { SettlementPriceCell } from './settlement-price-cell'; import { SettlementPriceCell } from './settlement-price-cell';
import { useDataProvider } from '@vegaprotocol/data-provider'; import { useDataProvider } from '@vegaprotocol/data-provider';
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
type SettlementAsset = type SettlementAsset =
MarketMaybeWithData['tradableInstrument']['instrument']['product']['settlementAsset']; MarketMaybeWithData['tradableInstrument']['instrument']['product']['settlementAsset'];
@@ -51,18 +47,33 @@ interface Row {
setlementDataSourceFilter: DataSourceFilterFragment | undefined; setlementDataSourceFilter: DataSourceFilterFragment | undefined;
tradingTerminationOracleId: string; tradingTerminationOracleId: string;
settlementAsset: SettlementAsset; settlementAsset: SettlementAsset;
successorMarketID: string | undefined | null; realisedPNL: string | undefined;
} }
export const Closed = () => { export const Closed = () => {
const { data: marketData, error } = useDataProvider({ const { pubKey } = useVegaWallet();
const {
data: marketData,
error,
reload,
} = useDataProvider({
dataProvider: closedMarketsWithDataProvider, dataProvider: closedMarketsWithDataProvider,
variables: undefined, variables: undefined,
}); });
const { data: positionData } = usePositionsQuery({
variables: {
partyIds: pubKey ? [pubKey] : [],
},
skip: !pubKey,
});
// find a position for each market and add the realised pnl to // find a position for each market and add the realised pnl to
// a normalized object // a normalized object
const rowData = compact(marketData).map((market) => { const rowData = compact(marketData).map((market) => {
const position = positionData?.positions?.edges?.find((edge) => {
return edge.node.market.id === market.id;
});
const instrument = market.tradableInstrument.instrument; const instrument = market.tradableInstrument.instrument;
const spec = const spec =
@@ -97,46 +108,26 @@ export const Closed = () => {
tradingTerminationOracleId: tradingTerminationOracleId:
instrument.product.dataSourceSpecForTradingTermination.id, instrument.product.dataSourceSpecForTradingTermination.id,
settlementAsset: instrument.product.settlementAsset, settlementAsset: instrument.product.settlementAsset,
successorMarketID: market.successorMarketID, realisedPNL: position?.node.realisedPNL,
}; };
return row; return row;
}); });
return ( return (
<div className="h-full relative"> <div className="h-full relative">
<ClosedMarketsDataGrid rowData={rowData} error={error} /> <ClosedMarketsDataGrid rowData={rowData} error={error} reload={reload} />
</div> </div>
); );
}; };
export const SuccessorMarketRenderer = ({
value,
}: VegaICellRendererParams<Row, 'successorMarketID'>) => {
const { data } = useDataProvider({
dataProvider: marketProvider,
variables: {
marketId: value || '',
},
skip: !value,
});
const onMarketClick = useMarketClickHandler();
return data ? (
<MarketNameCell
value={data.tradableInstrument.instrument.code}
data={data}
onMarketClick={onMarketClick}
/>
) : (
' - '
);
};
const ClosedMarketsDataGrid = ({ const ClosedMarketsDataGrid = ({
rowData, rowData,
error, error,
reload,
}: { }: {
rowData: Row[]; rowData: Row[];
error: Error | undefined; error: Error | undefined;
reload: () => void;
}) => { }) => {
const openAssetDialog = useAssetDetailsDialogStore((store) => store.open); const openAssetDialog = useAssetDetailsDialogStore((store) => store.open);
const colDefs = useMemo(() => { const colDefs = useMemo(() => {
@@ -208,11 +199,6 @@ const ClosedMarketsDataGrid = ({
}, },
}, },
}, },
{
headerName: t('Successor market'),
field: 'successorMarketID',
cellRenderer: 'SuccessorMarketRenderer',
},
{ {
headerName: t('Best bid'), headerName: t('Best bid'),
field: 'bestBidPrice', field: 'bestBidPrice',
@@ -268,11 +254,25 @@ const ClosedMarketsDataGrid = ({
/> />
), ),
}, },
{
headerName: t('Realised PNL'),
field: 'realisedPNL',
cellClass: 'font-mono ag-right-aligned-cell',
type: 'numericColumn',
valueFormatter: ({
value,
data,
}: VegaValueFormatterParams<Row, 'realisedPNL'>) => {
if (!value || !data) return '-';
return addDecimalsFormatNumber(value, data.decimalPlaces);
},
},
{ {
headerName: t('Settlement asset'), headerName: t('Settlement asset'),
field: 'settlementAsset', field: 'settlementAsset',
cellRenderer: ({ cellRenderer: ({
value, value,
data,
}: VegaValueFormatterParams<Row, 'settlementAsset'>) => ( }: VegaValueFormatterParams<Row, 'settlementAsset'>) => (
<button <button
className="underline" className="underline"
@@ -311,9 +311,7 @@ const ClosedMarketsDataGrid = ({
defaultColDef={{ defaultColDef={{
resizable: true, resizable: true,
minWidth: 100, minWidth: 100,
flex: 1,
}} }}
components={{ SuccessorMarketRenderer }}
overlayNoRowsTemplate={error ? error.message : t('No markets')} overlayNoRowsTemplate={error ? error.message : t('No markets')}
/> />
); );
@@ -15,20 +15,16 @@ export const MarketsPage = () => {
updateTitle(titlefy(['Markets'])); updateTitle(titlefy(['Markets']));
}, [updateTitle]); }, [updateTitle]);
return ( return (
<div className="h-full pt-0.5 pb-3 px-1.5"> <Tabs storageKey="console-markets">
<div className="h-full my-1 border border-default rounded-sm"> <Tab id="all-markets" name={t('All markets')}>
<Tabs storageKey="console-markets"> <Markets />
<Tab id="all-markets" name={t('All markets')}> </Tab>
<Markets /> <Tab id="proposed-markets" name={t('Proposed markets')}>
</Tab> <Proposed />
<Tab id="proposed-markets" name={t('Proposed markets')}> </Tab>
<Proposed /> <Tab id="closed-markets" name={t('Closed markets')}>
</Tab> <Closed />
<Tab id="closed-markets" name={t('Closed markets')}> </Tab>
<Closed /> </Tabs>
</Tab>
</Tabs>
</div>
</div>
); );
}; };
@@ -1,12 +1,11 @@
import { Button } from '@vegaprotocol/ui-toolkit'; import { Button } from '@vegaprotocol/ui-toolkit';
import { DepositsTable } from '@vegaprotocol/deposits'; import { useDepositDialog, DepositsTable } from '@vegaprotocol/deposits';
import { depositsProvider } from '@vegaprotocol/deposits'; import { depositsProvider } from '@vegaprotocol/deposits';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import { useDataProvider } from '@vegaprotocol/data-provider'; import { useDataProvider } from '@vegaprotocol/data-provider';
import { useVegaWallet } from '@vegaprotocol/wallet'; import { useVegaWallet } from '@vegaprotocol/wallet';
import { useRef } from 'react'; import { useRef } from 'react';
import type { AgGridReact } from 'ag-grid-react'; import type { AgGridReact } from 'ag-grid-react';
import { useSidebar, ViewType } from '../../components/sidebar';
export const DepositsContainer = () => { export const DepositsContainer = () => {
const gridRef = useRef<AgGridReact | null>(null); const gridRef = useRef<AgGridReact | null>(null);
@@ -16,7 +15,7 @@ export const DepositsContainer = () => {
variables: { partyId: pubKey || '' }, variables: { partyId: pubKey || '' },
skip: !pubKey, skip: !pubKey,
}); });
const setView = useSidebar((store) => store.setView); const openDepositDialog = useDepositDialog((state) => state.open);
return ( return (
<div className="h-full"> <div className="h-full">
<DepositsTable <DepositsTable
@@ -25,11 +24,11 @@ export const DepositsContainer = () => {
overlayNoRowsTemplate={error ? error.message : t('No deposits')} overlayNoRowsTemplate={error ? error.message : t('No deposits')}
/> />
{!isReadOnly && ( {!isReadOnly && (
<div className="h-auto flex justify-end p-2 bottom-0 right-0 absolute dark:bg-black/75 bg-white/75 rounded"> <div className="h-auto flex justify-end px-[11px] py-2 bottom-0 right-3 absolute dark:bg-black/75 bg-white/75 rounded">
<Button <Button
variant="primary" variant="primary"
size="sm" size="sm"
onClick={() => setView({ type: ViewType.Deposit })} onClick={() => openDepositDialog()}
data-testid="deposit-button" data-testid="deposit-button"
> >
{t('Deposit')} {t('Deposit')}
@@ -21,7 +21,6 @@ import {
ResizableGridPanel, ResizableGridPanel,
usePaneLayout, usePaneLayout,
} from '../../components/resizable-grid'; } from '../../components/resizable-grid';
import { ViewType, useSidebar } from '../../components/sidebar';
const WithdrawalsIndicator = () => { const WithdrawalsIndicator = () => {
const { ready } = useIncompleteWithdrawals(); const { ready } = useIncompleteWithdrawals();
@@ -29,14 +28,13 @@ const WithdrawalsIndicator = () => {
return null; return null;
} }
return ( return (
<span className="bg-vega-clight-500 dark:bg-vega-cdark-500 text-default rounded p-1 leading-none"> <span className="bg-vega-blue-450 text-white text-[10px] rounded p-[3px] pb-[2px] leading-none">
{ready.length} {ready.length}
</span> </span>
); );
}; };
export const Portfolio = () => { export const Portfolio = () => {
const { init, view, setView } = useSidebar();
const { updateTitle } = usePageTitleStore((store) => ({ const { updateTitle } = usePageTitleStore((store) => ({
updateTitle: store.updateTitle, updateTitle: store.updateTitle,
})); }));
@@ -45,16 +43,9 @@ export const Portfolio = () => {
updateTitle(titlefy([t('Portfolio')])); updateTitle(titlefy([t('Portfolio')]));
}, [updateTitle]); }, [updateTitle]);
// Make transfer sidebar open by default
useEffect(() => {
if (init && view === null) {
setView({ type: ViewType.Transfer });
}
}, [init, view, setView]);
const onMarketClick = useMarketClickHandler(true); const onMarketClick = useMarketClickHandler(true);
const [sizes, handleOnLayoutChange] = usePaneLayout({ id: 'portfolio' }); const [sizes, handleOnLayoutChange] = usePaneLayout({ id: 'portfolio' });
const wrapperClasses = 'p-0.5 h-full max-h-full flex flex-col'; const wrapperClasses = 'h-full max-h-full flex flex-col';
return ( return (
<div className={wrapperClasses}> <div className={wrapperClasses}>
<ResizableGrid vertical onChange={handleOnLayoutChange}> <ResizableGrid vertical onChange={handleOnLayoutChange}>
@@ -127,8 +118,8 @@ interface PortfolioGridChildProps {
const PortfolioGridChild = ({ children }: PortfolioGridChildProps) => { const PortfolioGridChild = ({ children }: PortfolioGridChildProps) => {
return ( return (
<section className="h-full p-1"> <section className="bg-white dark:bg-black w-full h-full">
<div className="border border-default h-full rounded-sm">{children}</div> {children}
</section> </section>
); );
}; };
@@ -1,6 +1,7 @@
import { Button } from '@vegaprotocol/ui-toolkit'; import { Button } from '@vegaprotocol/ui-toolkit';
import { import {
withdrawalProvider, withdrawalProvider,
useWithdrawalDialog,
WithdrawalsTable, WithdrawalsTable,
useIncompleteWithdrawals, useIncompleteWithdrawals,
} from '@vegaprotocol/withdraws'; } from '@vegaprotocol/withdraws';
@@ -8,7 +9,6 @@ import { useVegaWallet } from '@vegaprotocol/wallet';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import { useDataProvider } from '@vegaprotocol/data-provider'; import { useDataProvider } from '@vegaprotocol/data-provider';
import { VegaWalletContainer } from '../../components/vega-wallet-container'; import { VegaWalletContainer } from '../../components/vega-wallet-container';
import { ViewType, useSidebar } from '../../components/sidebar';
export const WithdrawalsContainer = () => { export const WithdrawalsContainer = () => {
const { pubKey, isReadOnly } = useVegaWallet(); const { pubKey, isReadOnly } = useVegaWallet();
@@ -17,7 +17,7 @@ export const WithdrawalsContainer = () => {
variables: { partyId: pubKey || '' }, variables: { partyId: pubKey || '' },
skip: !pubKey, skip: !pubKey,
}); });
const setView = useSidebar((store) => store.setView); const openWithdrawDialog = useWithdrawalDialog((state) => state.open);
const { ready, delayed } = useIncompleteWithdrawals(); const { ready, delayed } = useIncompleteWithdrawals();
return ( return (
@@ -32,11 +32,11 @@ export const WithdrawalsContainer = () => {
/> />
</div> </div>
{!isReadOnly && ( {!isReadOnly && (
<div className="h-auto flex justify-end p-2 bottom-0 right-0 absolute dark:bg-black/75 bg-white/75 rounded"> <div className="h-auto flex justify-end px-[11px] py-2 bottom-0 right-3 absolute dark:bg-black/75 bg-white/75 rounded">
<Button <Button
variant="primary" variant="primary"
size="sm" size="sm"
onClick={() => setView({ type: ViewType.Withdraw })} onClick={() => openWithdrawDialog()}
data-testid="withdraw-dialog-button" data-testid="withdraw-dialog-button"
> >
{t('Make withdrawal')} {t('Make withdrawal')}
@@ -0,0 +1,2 @@
export { Settings as default } from './settings';
export { SettingsButton } from './settings-button';
@@ -0,0 +1,16 @@
import { Icon, NavigationLink } from '@vegaprotocol/ui-toolkit';
import { t } from '@vegaprotocol/i18n';
import { Links, Routes } from '../../pages/client-router';
import { IconNames } from '@blueprintjs/icons';
export const SettingsButton = ({ withMobile }: { withMobile?: boolean }) => {
return (
<NavigationLink data-testid="Settings" to={Links[Routes.SETTINGS]()}>
{withMobile ? (
t('Settings')
) : (
<Icon name={IconNames.COG} className="!align-middle" />
)}
</NavigationLink>
);
};

Some files were not shown because too many files have changed in this diff Show More