feat(2033): reorganise token site (#2313)

* chore: make proposals the home page, redirect home to proposals

* chore: reorganise the remainder of the routes

* chore: small styling bug

* fix: nav links highlighted incorrectly

* feat: rename title, rename routes to new structure, add redirects

* chore: make navbar full width

* feat: new navbar bases on trading navbar

* chore: revert change to the width of the page

* chore: make navbar reactive and support draw

* chore: move draw into its' own file

* chore: move nav into toolkit

* style: lint

* chore: trading to use navbar

* fix: uppercase navbar title

* chore: add test

* fix: merge issue

* style: lint

* test: adjust test URLs

* test: more route adjsuting

* test: fix route name

* test: change URL to be new url

* test: more path corrections

* test: more path fixes

* style: lint

* test: minor test fixes

* test: more test fixes

* test: fix incorrect paths

* test: green build plz

* chore: adjust name as per PR comments

* test: fix path

* test: fix incorrect path

* test: final test fix

* test: plz green
This commit is contained in:
Dexter Edwards 2022-12-06 16:00:37 +00:00 committed by GitHub
parent 7c15ee6c01
commit 3486244137
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 637 additions and 507 deletions

View File

@ -52,7 +52,6 @@ context(
function () { function () {
before('connect wallets and set approval limit', function () { before('connect wallets and set approval limit', function () {
cy.visit('/'); cy.visit('/');
cy.verify_page_header('The $VEGA token');
cy.get_network_parameters().then((network_parameters) => { cy.get_network_parameters().then((network_parameters) => {
cy.wrap( cy.wrap(
network_parameters['spam.protection.proposal.min.tokens'] / network_parameters['spam.protection.proposal.min.tokens'] /
@ -132,7 +131,7 @@ context(
cy.wait_for_spinner(); cy.wait_for_spinner();
cy.connectVegaWallet(); cy.connectVegaWallet();
cy.ethereum_wallet_connect(); cy.ethereum_wallet_connect();
cy.navigate_to('governance'); cy.navigate_to('proposals');
cy.wait_for_spinner(); cy.wait_for_spinner();
}); });
@ -207,7 +206,7 @@ context(
let arrayOfProposals = []; let arrayOfProposals = [];
cy.navigate_to('governance'); cy.navigate_to('proposals');
cy.wait_for_spinner(); cy.wait_for_spinner();
cy.get(proposalDetailsTitle) cy.get(proposalDetailsTitle)
.each((proposalTitleElement) => { .each((proposalTitleElement) => {
@ -223,16 +222,16 @@ context(
it('Able to submit a valid freeform proposal - with minimum required tokens associated - but also staked', function () { it('Able to submit a valid freeform proposal - with minimum required tokens associated - but also staked', function () {
cy.ensure_specified_unstaked_tokens_are_associated('2'); cy.ensure_specified_unstaked_tokens_are_associated('2');
cy.navigate_to_page_if_not_already_loaded('governance'); cy.navigate_to_page_if_not_already_loaded('proposals');
cy.get(vegaWalletUnstakedBalance, txTimeout).should('contain', '2'); cy.get(vegaWalletUnstakedBalance, txTimeout).should('contain', '2');
cy.navigate_to('staking'); cy.navigate_to('validators');
cy.wait_for_spinner(); cy.wait_for_spinner();
cy.click_on_validator_from_list(0); cy.click_on_validator_from_list(0);
cy.staking_validator_page_add_stake('2'); cy.staking_validator_page_add_stake('2');
cy.get(vegaWalletStakedBalances, txTimeout).should('contain', '2'); cy.get(vegaWalletStakedBalances, txTimeout).should('contain', '2');
cy.navigate_to('governance'); cy.navigate_to('proposals');
cy.wait_for_spinner(); cy.wait_for_spinner();
cy.go_to_make_new_proposal(governanceProposalType.FREEFORM); cy.go_to_make_new_proposal(governanceProposalType.FREEFORM);
cy.enter_unique_freeform_proposal_body('50', generateProposalTitle()); cy.enter_unique_freeform_proposal_body('50', generateProposalTitle());
@ -320,14 +319,14 @@ context(
cy.ensure_specified_unstaked_tokens_are_associated( cy.ensure_specified_unstaked_tokens_are_associated(
tokensRequiredToAchieveResult tokensRequiredToAchieveResult
); );
cy.navigate_to_page_if_not_already_loaded('governance'); cy.navigate_to_page_if_not_already_loaded('proposals');
cy.get('@submittedProposal').within(() => cy.get('@submittedProposal').within(() =>
cy.get(viewProposalButton).click() cy.get(viewProposalButton).click()
); );
cy.get_proposal_information_from_table('Participation met') cy.get_proposal_information_from_table('Participation met')
.contains('👍') .contains('👍')
.should('be.visible'); .should('be.visible');
cy.navigate_to('governance'); cy.navigate_to('proposals');
cy.wait_for_spinner(); cy.wait_for_spinner();
cy.get('@submittedProposal').within(() => cy.get('@submittedProposal').within(() =>
cy.get(voteStatus).should('have.text', 'Participation met') cy.get(voteStatus).should('have.text', 'Participation met')
@ -378,7 +377,7 @@ context(
cy.wait_for_proposal_submitted(); cy.wait_for_proposal_submitted();
cy.wait_for_proposal_sync(); cy.wait_for_proposal_sync();
cy.navigate_to('governance'); cy.navigate_to('proposals');
cy.wait_for_spinner(); cy.wait_for_spinner();
cy.get_submitted_proposal_from_proposal_list(proposalTitle).within( cy.get_submitted_proposal_from_proposal_list(proposalTitle).within(
() => cy.get(viewProposalButton).click() () => cy.get(viewProposalButton).click()
@ -523,7 +522,7 @@ context(
cy.ensure_specified_unstaked_tokens_are_associated( cy.ensure_specified_unstaked_tokens_are_associated(
tokensRequiredToAchieveResult tokensRequiredToAchieveResult
); );
cy.navigate_to_page_if_not_already_loaded('governance'); cy.navigate_to_page_if_not_already_loaded('proposals');
cy.get('@submittedProposal').within(() => cy.get('@submittedProposal').within(() =>
cy.get(viewProposalButton).click() cy.get(viewProposalButton).click()
); );
@ -611,7 +610,7 @@ context(
cy.contains('Proposal rejected', proposalTimeout).should('be.visible'); cy.contains('Proposal rejected', proposalTimeout).should('be.visible');
cy.get(dialogCloseButton).click(); cy.get(dialogCloseButton).click();
cy.wait_for_proposal_sync(); cy.wait_for_proposal_sync();
cy.navigate_to('governance'); cy.navigate_to('proposals');
cy.wait_for_spinner(); cy.wait_for_spinner();
cy.get(rejectProposalsLink).click().wait_for_spinner(); cy.get(rejectProposalsLink).click().wait_for_spinner();
cy.get('@rawProposal').then((rawProposal) => { cy.get('@rawProposal').then((rawProposal) => {
@ -808,7 +807,7 @@ context(
cy.get(newProposalSubmitButton).should('be.visible').click(); cy.get(newProposalSubmitButton).should('be.visible').click();
cy.wait_for_proposal_submitted(); cy.wait_for_proposal_submitted();
cy.wait_for_proposal_sync(); cy.wait_for_proposal_sync();
cy.navigate_to('governance'); cy.navigate_to('proposals');
cy.wait_for_spinner(); cy.wait_for_spinner();
} }
@ -820,7 +819,7 @@ context(
cy.wait_for_proposal_submitted(); cy.wait_for_proposal_submitted();
cy.wait_for_proposal_sync(); cy.wait_for_proposal_sync();
cy.get(proposalDetailsTitle).invoke('text').as('proposalTitle'); cy.get(proposalDetailsTitle).invoke('text').as('proposalTitle');
cy.navigate_to('governance'); cy.navigate_to('proposals');
cy.wait_for_spinner(); cy.wait_for_spinner();
} }

View File

@ -41,7 +41,6 @@ context(
function () { function () {
before('connect wallets and set approval limit', function () { before('connect wallets and set approval limit', function () {
cy.visit('/'); cy.visit('/');
cy.verify_page_header('The $VEGA token');
cy.vega_wallet_set_specified_approval_amount('1000'); cy.vega_wallet_set_specified_approval_amount('1000');
}); });
@ -50,7 +49,7 @@ context(
cy.wait_for_spinner(); cy.wait_for_spinner();
cy.connectVegaWallet(); cy.connectVegaWallet();
cy.ethereum_wallet_connect(); cy.ethereum_wallet_connect();
cy.navigate_to('governance'); cy.navigate_to('proposals');
cy.wait_for_spinner(); cy.wait_for_spinner();
}); });
@ -74,7 +73,7 @@ context(
}); });
it('Unable to submit network parameter with missing/invalid fields', function () { it('Unable to submit network parameter with missing/invalid fields', function () {
cy.navigate_to_page_if_not_already_loaded('governance'); cy.navigate_to_page_if_not_already_loaded('proposals');
cy.go_to_make_new_proposal(governanceProposalType.NETWORK_PARAMETER); cy.go_to_make_new_proposal(governanceProposalType.NETWORK_PARAMETER);
cy.get(newProposalSubmitButton).should('be.visible').click(); cy.get(newProposalSubmitButton).should('be.visible').click();
cy.get(inputError).should('have.length', 3); cy.get(inputError).should('have.length', 3);
@ -99,7 +98,7 @@ context(
}); });
it('Unable to submit network parameter proposal with vote deadline above enactment deadline', function () { it('Unable to submit network parameter proposal with vote deadline above enactment deadline', function () {
cy.navigate_to_page_if_not_already_loaded('governance'); cy.navigate_to_page_if_not_already_loaded('proposals');
cy.go_to_make_new_proposal(governanceProposalType.NETWORK_PARAMETER); cy.go_to_make_new_proposal(governanceProposalType.NETWORK_PARAMETER);
cy.get(newProposalTitle).type('Test update network parameter proposal'); cy.get(newProposalTitle).type('Test update network parameter proposal');
cy.get(newProposalDescription).type('invalid deadlines'); cy.get(newProposalDescription).type('invalid deadlines');

View File

@ -33,7 +33,6 @@ context(
// 2001-STKE-002, 2001-STKE-032 // 2001-STKE-002, 2001-STKE-032
before('visit staking tab and connect vega wallet', function () { before('visit staking tab and connect vega wallet', function () {
cy.visit('/'); cy.visit('/');
cy.verify_page_header('The $VEGA token');
cy.vega_wallet_set_specified_approval_amount('1000'); cy.vega_wallet_set_specified_approval_amount('1000');
}); });
@ -46,7 +45,7 @@ context(
cy.connectVegaWallet(); cy.connectVegaWallet();
cy.ethereum_wallet_connect(); cy.ethereum_wallet_connect();
cy.vega_wallet_teardown(); cy.vega_wallet_teardown();
cy.navigate_to('staking'); cy.navigate_to('validators');
cy.wait_for_spinner(); cy.wait_for_spinner();
} }
); );
@ -95,7 +94,7 @@ context(
.contains(2.0, epochTimeout) .contains(2.0, epochTimeout)
.should('be.visible'); .should('be.visible');
cy.navigate_to('staking'); cy.navigate_to('validators');
cy.validate_validator_list_total_stake_and_share('0', '2.00', '100%'); cy.validate_validator_list_total_stake_and_share('0', '2.00', '100%');
}); });
@ -142,14 +141,14 @@ context(
.contains(2.0, epochTimeout) .contains(2.0, epochTimeout)
.should('be.visible'); .should('be.visible');
cy.navigate_to('staking'); cy.navigate_to('validators');
cy.validate_validator_list_total_stake_and_share('0', '2.00', '100%'); cy.validate_validator_list_total_stake_and_share('0', '2.00', '100%');
}); });
it('Able to stake against a validator - using vega from both wallet and vesting contract', function () { it('Able to stake against a validator - using vega from both wallet and vesting contract', function () {
cy.staking_page_associate_tokens('3', { type: 'contract' }); cy.staking_page_associate_tokens('3', { type: 'contract' });
cy.navigate_to('staking'); cy.navigate_to('validators');
cy.staking_page_associate_tokens('4', { type: 'wallet' }); cy.staking_page_associate_tokens('4', { type: 'wallet' });
cy.get(vegaWalletUnstakedBalance, txTimeout).should( cy.get(vegaWalletUnstakedBalance, txTimeout).should(
@ -202,7 +201,7 @@ context(
.contains(6.0, epochTimeout) .contains(6.0, epochTimeout)
.should('be.visible'); .should('be.visible');
cy.navigate_to('staking'); cy.navigate_to('validators');
cy.validate_validator_list_total_stake_and_share('0', '6.00', '100%'); cy.validate_validator_list_total_stake_and_share('0', '6.00', '100%');
}); });
@ -232,7 +231,7 @@ context(
.parent() .parent()
.should('contain', 2.0, txTimeout); .should('contain', 2.0, txTimeout);
cy.navigate_to('staking'); cy.navigate_to('validators');
cy.click_on_validator_from_list(1); cy.click_on_validator_from_list(1);
@ -253,7 +252,7 @@ context(
.eq(1) .eq(1)
.should('contain', 1.0, txTimeout); .should('contain', 1.0, txTimeout);
cy.navigate_to('staking'); cy.navigate_to('validators');
cy.wait_for_spinner(); cy.wait_for_spinner();
cy.get(`[row-id="${0}"]`).within(() => { cy.get(`[row-id="${0}"]`).within(() => {
@ -310,7 +309,7 @@ context(
txTimeout txTimeout
); );
cy.navigate_to('staking'); cy.navigate_to('validators');
// 2001-STKE-040 // 2001-STKE-040
cy.click_on_validator_from_list(0); cy.click_on_validator_from_list(0);
@ -343,7 +342,7 @@ context(
cy.get(totalStake, epochTimeout).should('contain.text', '2'); cy.get(totalStake, epochTimeout).should('contain.text', '2');
cy.get(stakeShare, epochTimeout).should('have.text', '100%'); cy.get(stakeShare, epochTimeout).should('have.text', '100%');
cy.navigate_to('staking'); cy.navigate_to('validators');
cy.validate_validator_list_total_stake_and_share('0', '2.00', '100%'); cy.validate_validator_list_total_stake_and_share('0', '2.00', '100%');
}); });
@ -370,7 +369,7 @@ context(
txTimeout txTimeout
); );
cy.navigate_to('staking'); cy.navigate_to('validators');
cy.click_on_validator_from_list('0'); cy.click_on_validator_from_list('0');
@ -399,7 +398,7 @@ context(
txTimeout txTimeout
); );
cy.navigate_to('staking'); cy.navigate_to('validators');
cy.validate_validator_list_total_stake_and_share('0', '0.00', '0%'); cy.validate_validator_list_total_stake_and_share('0', '0.00', '0%');
}); });
@ -429,7 +428,7 @@ context(
txTimeout txTimeout
); );
cy.navigate_to('staking'); cy.navigate_to('validators');
cy.click_on_validator_from_list(0); cy.click_on_validator_from_list(0);
@ -470,7 +469,7 @@ context(
txTimeout txTimeout
); );
cy.navigate_to('staking'); cy.navigate_to('validators');
cy.click_on_validator_from_list(0); cy.click_on_validator_from_list(0);
@ -536,7 +535,7 @@ context(
txTimeout txTimeout
); );
cy.navigate_to('staking'); cy.navigate_to('validators');
cy.validate_validator_list_total_stake_and_share('0', '0.00', '0%'); cy.validate_validator_list_total_stake_and_share('0', '0.00', '0%');
}); });
@ -590,7 +589,7 @@ context(
txTimeout txTimeout
); );
cy.navigate_to('staking'); cy.navigate_to('validators');
cy.validate_validator_list_total_stake_and_share('0', '0.00', '0%'); cy.validate_validator_list_total_stake_and_share('0', '0.00', '0%');
}); });
@ -637,7 +636,7 @@ context(
.should('contain', 2.0, txTimeout) .should('contain', 2.0, txTimeout)
.and('contain', partValidatorId); .and('contain', partValidatorId);
cy.navigate_to('staking'); cy.navigate_to('validators');
cy.validate_validator_list_total_stake_and_share('0', '2.00', '100%'); cy.validate_validator_list_total_stake_and_share('0', '2.00', '100%');
}); });

View File

@ -7,11 +7,11 @@ const vegaWalletUnstakedBalance =
'[data-testid="vega-wallet-balance-unstaked"]'; '[data-testid="vega-wallet-balance-unstaked"]';
const txTimeout = Cypress.env('txTimeout'); const txTimeout = Cypress.env('txTimeout');
const vegaWalletPublicKeyShort = Cypress.env('vegaWalletPublicKeyShort'); const vegaWalletPublicKeyShort = Cypress.env('vegaWalletPublicKeyShort');
const ethWalletAssociateButton = '[href="/staking/associate"]'; const ethWalletAssociateButton = '[href="/validators/associate"]';
const associateWalletRadioButton = '[data-testid="associate-radio-wallet"]'; const associateWalletRadioButton = '[data-testid="associate-radio-wallet"]';
const tokenAmountInputBox = '[data-testid="token-amount-input"]'; const tokenAmountInputBox = '[data-testid="token-amount-input"]';
const tokenSubmitButton = '[data-testid="token-input-submit-button"]'; const tokenSubmitButton = '[data-testid="token-input-submit-button"]';
const ethWalletDissociateButton = '[href="/staking/disassociate"]'; const ethWalletDissociateButton = '[href="/validators/disassociate"]';
const vestingContractSection = '[data-testid="vega-in-vesting-contract"]'; const vestingContractSection = '[data-testid="vega-in-vesting-contract"]';
const vegaInWalletSection = '[data-testid="vega-in-wallet"]'; const vegaInWalletSection = '[data-testid="vega-in-wallet"]';
const connectedVegaKey = '[data-testid="connected-vega-key"]'; const connectedVegaKey = '[data-testid="connected-vega-key"]';
@ -27,7 +27,6 @@ context(
function () { function () {
before('visit staking tab and connect vega wallet', function () { before('visit staking tab and connect vega wallet', function () {
cy.visit('/'); cy.visit('/');
cy.verify_page_header('The $VEGA token');
cy.vega_wallet_set_specified_approval_amount('1000'); cy.vega_wallet_set_specified_approval_amount('1000');
}); });
@ -40,7 +39,7 @@ context(
cy.connectVegaWallet(); cy.connectVegaWallet();
cy.ethereum_wallet_connect(); cy.ethereum_wallet_connect();
cy.vega_wallet_teardown(); cy.vega_wallet_teardown();
cy.navigate_to('staking'); cy.navigate_to('validators');
cy.wait_for_spinner(); cy.wait_for_spinner();
} }
); );
@ -257,7 +256,7 @@ context(
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 52); cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 52);
}); });
cy.navigate_to('staking'); cy.navigate_to('validators');
cy.staking_page_disassociate_tokens('9', { type: 'wallet' }); cy.staking_page_disassociate_tokens('9', { type: 'wallet' });
cy.get(vegaInWalletSection).within(() => { cy.get(vegaInWalletSection).within(() => {

View File

@ -33,7 +33,7 @@ context(
beforeEach('Navigate to withdrawal page', function () { beforeEach('Navigate to withdrawal page', function () {
cy.reload(); cy.reload();
cy.visit('/'); cy.visit('/');
cy.navigate_to('withdrawals'); cy.navigate_to('withdraw');
cy.wait_for_spinner(); cy.wait_for_spinner();
cy.connectVegaWallet(); cy.connectVegaWallet();
cy.ethereum_wallet_connect(); cy.ethereum_wallet_connect();

View File

@ -9,12 +9,12 @@ context(
{ tags: '@smoke' }, { tags: '@smoke' },
function () { function () {
before('navigate to governance page', function () { before('navigate to governance page', function () {
cy.visit('/').navigate_to('governance'); cy.visit('/').navigate_to('proposals');
}); });
describe('with no network change proposals', function () { describe('with no network change proposals', function () {
it('should have governance tab highlighted', function () { it('should have governance tab highlighted', function () {
cy.verify_tab_highlighted('governance'); cy.verify_tab_highlighted('proposals');
}); });
it('should have GOVERNANCE header visible', function () { it('should have GOVERNANCE header visible', function () {
@ -49,7 +49,7 @@ context(
.should('be.visible') .should('be.visible')
.and('have.text', 'New proposal') .and('have.text', 'New proposal')
.and('have.attr', 'href') .and('have.attr', 'href')
.and('equal', '/governance/propose'); .and('equal', '/proposals/propose');
}); });
// Skipping this test for now, the new proposal button no longer takes a user directly // Skipping this test for now, the new proposal button no longer takes a user directly
@ -60,7 +60,7 @@ context(
cy.get(connectToVegaWalletButton) cy.get(connectToVegaWalletButton)
.should('be.visible') .should('be.visible')
.and('have.text', 'Connect Vega wallet'); .and('have.text', 'Connect Vega wallet');
cy.navigate_to('governance'); cy.navigate_to('proposals');
cy.wait_for_spinner(); cy.wait_for_spinner();
}); });
}); });

View File

@ -1,10 +1,10 @@
const navSection = 'nav'; const navSection = 'nav';
const navHome = '[href="/"]'; const navVesting = '[href="/token/tranches"]';
const navVesting = '[href="/vesting"]'; const navToken = '[href="/token"]';
const navStaking = '[href="/staking"]'; const navStaking = '[href="/validators"]';
const navRewards = '[href="/rewards"]'; const navRewards = '[href="/rewards"]';
const navWithdraw = '[href="/withdrawals"]'; const navWithdraw = '[href="/token/withdraw"]';
const navGovernance = '[href="/governance"]'; const navGovernance = '[href="/proposals"]';
const tokenDetailsTable = '.token-details'; const tokenDetailsTable = '.token-details';
const address = '[data-testid="token-address"]'; const address = '[data-testid="token-address"]';
@ -25,7 +25,7 @@ const vegaTokenContractAddress = Cypress.env('vegaTokenContractAddress');
context('Home Page - verify elements on page', { tags: '@smoke' }, function () { context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
before('visit token home page', function () { before('visit token home page', function () {
cy.visit('/'); cy.visit('/token');
}); });
describe('with wallets disconnected', function () { describe('with wallets disconnected', function () {
@ -34,9 +34,9 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
}); });
describe('Navigation tabs', function () { describe('Navigation tabs', function () {
it('should have HOME tab', function () { it('should have TOKEN tab', function () {
cy.get(navSection).within(() => { cy.get(navSection).within(() => {
cy.get(navHome).should('be.visible'); cy.get(navToken).should('be.visible');
}); });
}); });
it('should have VESTING tab', function () { it('should have VESTING tab', function () {
@ -106,14 +106,14 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
cy.get(tranchesLink) cy.get(tranchesLink)
.should('be.visible') .should('be.visible')
.and('have.attr', 'href') .and('have.attr', 'href')
.and('equal', '/tranches'); .and('equal', '/token/tranches');
}); });
it('should have REDEEM button', function () { it('should have REDEEM button', function () {
cy.get(redeemBtn) cy.get(redeemBtn)
.should('be.visible') .should('be.visible')
.parent() .parent()
.should('have.attr', 'href') .should('have.attr', 'href')
.and('equal', '/vesting'); .and('equal', '/token/redeem');
}); });
it('should have GET VEGA WALLET link', function () { it('should have GET VEGA WALLET link', function () {
cy.get(getVegaWalletLink) cy.get(getVegaWalletLink)
@ -125,21 +125,21 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
cy.get(associateVegaLink) cy.get(associateVegaLink)
.should('be.visible') .should('be.visible')
.and('have.attr', 'href') .and('have.attr', 'href')
.and('equal', '/staking/associate'); .and('equal', '/validators/associate');
}); });
it('should have STAKING button', function () { it('should have STAKING button', function () {
cy.get(stakingBtn) cy.get(stakingBtn)
.should('be.visible') .should('be.visible')
.parent() .parent()
.should('have.attr', 'href') .should('have.attr', 'href')
.and('equal', '/staking'); .and('equal', '/validators');
}); });
it('should have GOVERNANCE button', function () { it('should have GOVERNANCE button', function () {
cy.get(governanceBtn) cy.get(governanceBtn)
.should('be.visible') .should('be.visible')
.parent() .parent()
.should('have.attr', 'href') .should('have.attr', 'href')
.and('equal', '/governance'); .and('equal', '/proposals');
}); });
}); });
}); });

View File

@ -15,13 +15,13 @@ const stakeNumberRegex = /^\d*\.?\d*$/;
context('Staking Page - verify elements on page', function () { context('Staking Page - verify elements on page', function () {
before('navigate to staking page', function () { before('navigate to staking page', function () {
cy.visit('/').navigate_to('staking'); cy.visit('/').navigate_to('validators');
}); });
describe('with wallets disconnected', { tags: '@smoke' }, function () { describe('with wallets disconnected', { tags: '@smoke' }, function () {
describe('description section', function () { describe('description section', function () {
it('Should have staking tab highlighted', function () { it('Should have staking tab highlighted', function () {
cy.verify_tab_highlighted('staking'); cy.verify_tab_highlighted('validators');
}); });
it('Should have STAKING ON VEGA header visible', function () { it('Should have STAKING ON VEGA header visible', function () {

View File

@ -5,11 +5,10 @@ context(
'Vesting Page - verify elements on page', 'Vesting Page - verify elements on page',
{ tags: '@smoke' }, { tags: '@smoke' },
function () { function () {
before('navigate to vesting page', function () {
cy.visit('/').navigate_to('vesting');
});
describe('with wallets disconnected', function () { describe('with wallets disconnected', function () {
before('navigate to vesting page', function () {
cy.visit('/').navigate_to('vesting');
});
it('should have vesting tab highlighted', function () { it('should have vesting tab highlighted', function () {
cy.verify_tab_highlighted('vesting'); cy.verify_tab_highlighted('vesting');
}); });
@ -32,15 +31,16 @@ context(
describe('with eth wallet connected', function () { describe('with eth wallet connected', function () {
before('connect eth wallet', function () { before('connect eth wallet', function () {
cy.ethereum_wallet_connect(); cy.ethereum_wallet_connect();
cy.visit('/');
}); });
// 1005-VEST-001 // 1005-VEST-001
// 1005-VEST-002 // 1005-VEST-002
it('Able to view tranches', function () { it('Able to view tranches', function () {
cy.get('[href="/tranches"]') cy.get('[href="/token/tranches"]')
.should('have.text', 'all tranches') .should('have.text', 'Supply & Vesting')
.click(); .click();
cy.url().should('include', '/tranches'); cy.url().should('include', '/token/tranches');
cy.get('h1').should('contain.text', 'Vesting tranches'); cy.get('h1').should('contain.text', 'Vesting tranches');
}); });
}); });

View File

@ -2,8 +2,8 @@ const walletContainer = '[data-testid="ethereum-wallet"]';
const walletHeader = '[data-testid="wallet-header"] h1'; const walletHeader = '[data-testid="wallet-header"] h1';
const connectToEthButton = '[data-testid="connect-to-eth-wallet-button"]'; const connectToEthButton = '[data-testid="connect-to-eth-wallet-button"]';
const connectorList = '[data-testid="web3-connector-list"]'; const connectorList = '[data-testid="web3-connector-list"]';
const associate = '[href="/staking/associate"]'; const associate = '[href="/validators/associate"]';
const disassociate = '[href="/staking/disassociate"]'; const disassociate = '[href="/validators/disassociate"]';
const disconnect = '[data-testid="disconnect-from-eth-wallet-button"]'; const disconnect = '[data-testid="disconnect-from-eth-wallet-button"]';
const accountNo = '[data-testid="ethereum-account-truncated"]'; const accountNo = '[data-testid="ethereum-account-truncated"]';
const currencyTitle = '[data-testid="currency-title"]'; const currencyTitle = '[data-testid="currency-title"]';

View File

@ -18,8 +18,8 @@ const walletName = '[data-testid="wallet-name"]';
const currencyTitle = '[data-testid="currency-title"]'; const currencyTitle = '[data-testid="currency-title"]';
const currencyValue = '[data-testid="currency-value"]'; const currencyValue = '[data-testid="currency-value"]';
const vegaUnstaked = '[data-testid="vega-wallet-balance-unstaked"] .text-right'; const vegaUnstaked = '[data-testid="vega-wallet-balance-unstaked"] .text-right';
const governanceBtn = '[href="/governance"]'; const governanceBtn = '[href="/proposals"]';
const stakingBtn = '[href="/staking"]'; const stakingBtn = '[href="/validators"]';
const manageLink = '[data-testid="manage-vega-wallet"]'; const manageLink = '[data-testid="manage-vega-wallet"]';
const dialogVegaKey = '[data-testid="vega-public-key-full"]'; const dialogVegaKey = '[data-testid="vega-public-key-full"]';
const dialogDisconnectBtn = '[data-testid="disconnect"]'; const dialogDisconnectBtn = '[data-testid="disconnect"]';

View File

@ -5,12 +5,12 @@ context(
{ tags: '@smoke' }, { tags: '@smoke' },
function () { function () {
before('navigate to withdrawals page', function () { before('navigate to withdrawals page', function () {
cy.visit('/').navigate_to('withdrawals'); cy.visit('/').navigate_to('withdraw');
}); });
describe('with wallets disconnected', function () { describe('with wallets disconnected', function () {
it('should have withdraw tab highlighted', function () { it('should have withdraw tab highlighted', function () {
cy.verify_tab_highlighted('withdrawals'); cy.verify_tab_highlighted('withdraw');
}); });
it('should have WITHDRAW header visible', function () { it('should have WITHDRAW header visible', function () {

View File

@ -8,13 +8,13 @@ Cypress.Commands.add(
const navigation = { const navigation = {
section: 'nav', section: 'nav',
home: '[href="/"]', vesting: '[href="/token/redeem"]',
vesting: '[href="/vesting"]', validators: '[href="/validators"]',
staking: '[href="/staking"]',
rewards: '[href="/rewards"]', rewards: '[href="/rewards"]',
withdrawals: '[href="/withdrawals"]', withdraw: '[href="/token/withdraw"]',
governance: '[href="/governance"]', proposals: '[href="/proposals"]',
pageSpinner: '[data-testid="splash-loader"]', pageSpinner: '[data-testid="splash-loader"]',
token: '[href="/token"]',
}; };
Cypress.Commands.add('navigate_to', (page) => { Cypress.Commands.add('navigate_to', (page) => {

View File

@ -172,9 +172,9 @@ Cypress.Commands.add('get_sort_order_of_supplied_array', (suppliedArray) => {
}); });
Cypress.Commands.add('go_to_make_new_proposal', (proposalType) => { Cypress.Commands.add('go_to_make_new_proposal', (proposalType) => {
cy.navigate_to_page_if_not_already_loaded('governance'); cy.navigate_to_page_if_not_already_loaded('proposals');
cy.get(newProposalButton).should('be.visible').click(); cy.get(newProposalButton).should('be.visible').click();
cy.url().should('include', '/governance/propose'); cy.url().should('include', '/proposals/propose');
cy.wait_for_spinner(); cy.wait_for_spinner();
cy.get('li').contains(proposalType).click(); cy.get('li').contains(proposalType).click();
}); });

View File

@ -3,8 +3,8 @@ const tokenSubmitButton = '[data-testid="token-input-submit-button"]';
const tokenInputApprove = '[data-testid="token-input-approve-button"]'; const tokenInputApprove = '[data-testid="token-input-approve-button"]';
const addStakeRadioButton = '[data-testid="add-stake-radio"]'; const addStakeRadioButton = '[data-testid="add-stake-radio"]';
const removeStakeRadioButton = '[data-testid="remove-stake-radio"]'; const removeStakeRadioButton = '[data-testid="remove-stake-radio"]';
const ethWalletAssociateButton = '[href="/staking/associate"]'; const ethWalletAssociateButton = '[href="/validators/associate"]';
const ethWalletDissociateButton = '[href="/staking/disassociate"]'; const ethWalletDissociateButton = '[href="/validators/disassociate"]';
const vegaWalletUnstakedBalance = const vegaWalletUnstakedBalance =
'[data-testid="vega-wallet-balance-unstaked"]'; '[data-testid="vega-wallet-balance-unstaked"]';
const vegaWalletAssociatedBalance = '[data-testid="currency-value"]'; const vegaWalletAssociatedBalance = '[data-testid="currency-value"]';

View File

@ -88,7 +88,7 @@ const Web3Container = ({
<AppLoader> <AppLoader>
<BalanceManager> <BalanceManager>
<> <>
<div className="app w-full max-w-[1500px] mx-auto grid grid-rows-[1fr_min-content] min-h-full border-neutral-700 lg:border-l lg:border-r lg:text-body-large"> <div className="app w-full max-w-[1500px] mx-auto grid grid-rows-[min-content_1fr_min-content] min-h-full border-neutral-700 lg:border-l lg:border-r lg:text-body-large">
<TemplateSidebar sidebar={sideBar}> <TemplateSidebar sidebar={sideBar}>
<AppRouter /> <AppRouter />
</TemplateSidebar> </TemplateSidebar>

View File

@ -164,12 +164,12 @@ const ConnectedKey = () => {
)} )}
</section> </section>
<WalletCardActions> <WalletCardActions>
<Link className="flex-1" to={`${Routes.STAKING}/associate`}> <Link className="flex-1" to={`${Routes.VALIDATORS}/associate`}>
<Button size="sm" fill={true}> <Button size="sm" fill={true}>
{t('associate')} {t('associate')}
</Button> </Button>
</Link> </Link>
<Link className="flex-1" to={`${Routes.STAKING}/disassociate`}> <Link className="flex-1" to={`${Routes.VALIDATORS}/disassociate`}>
<Button size="sm" fill={true}> <Button size="sm" fill={true}>
{t('disassociate')} {t('disassociate')}
</Button> </Button>

View File

@ -0,0 +1,124 @@
import classNames from 'classnames';
import { NavLink } from 'react-router-dom';
import {
AppStateActionType,
useAppState,
} from '../../contexts/app-state/app-state-context';
import * as Dialog from '@radix-ui/react-dialog';
import { EthWallet } from '../eth-wallet';
import { VegaWallet } from '../vega-wallet';
interface Route {
name: string;
path: string;
}
const DrawerSection = ({ children }: { children: React.ReactNode }) => (
<div className="px-4 my-4">{children}</div>
);
const IconLine = ({ inverted }: { inverted: boolean }) => (
<span className={`block w-6 h-[2px] ${inverted ? 'bg-black' : 'bg-white'}`} />
);
const DrawerNavLinks = ({
isInverted,
routes,
}: {
isInverted?: boolean;
routes: Route[];
}) => {
const { appDispatch } = useAppState();
const linkProps = {
end: true,
onClick: () =>
appDispatch({ type: AppStateActionType.SET_DRAWER, isOpen: false }),
};
const navClasses = classNames('flex flex-col');
return (
<nav className={navClasses}>
{routes.map(({ name, path }) => {
return (
<NavLink
{...linkProps}
to={{ pathname: path }}
className={({ isActive }) =>
classNames({
'bg-vega-yellow text-black': !isInverted && isActive,
'bg-transparent text-white hover:text-vega-yellow':
!isInverted && !isActive,
'bg-black text-white': isInverted && isActive,
'bg-transparent text-black hover:text-white':
isInverted && !isActive,
'border-t border-white p-4': true,
})
}
>
{name}
</NavLink>
);
})}
</nav>
);
};
export const NavDrawer = ({
inverted,
routes,
}: {
inverted: boolean;
routes: Route[];
}) => {
const { appState, appDispatch } = useAppState();
const drawerContentClasses = classNames(
'drawer-content', // needed for css animation
// Positions the modal in the center of screen
'fixed w-[80vw] max-w-[420px] top-0 right-0',
'flex flex-col flex-nowrap justify-between h-full bg-banner overflow-y-scroll border-l border-white',
'bg-black text-neutral-200'
);
return (
<>
<button
onClick={() =>
appDispatch({
type: AppStateActionType.SET_DRAWER,
isOpen: true,
})
}
className="flex flex-col flex-nowrap gap-1"
>
<IconLine inverted={inverted} />
<IconLine inverted={inverted} />
<IconLine inverted={inverted} />
</button>
<Dialog.Root
open={appState.drawerOpen}
onOpenChange={(isOpen) =>
appDispatch({
type: AppStateActionType.SET_DRAWER,
isOpen,
})
}
>
<Dialog.Portal>
<Dialog.Overlay className="fixed inset-0 bg-white/15" />
<Dialog.Content className={drawerContentClasses}>
<div>
<DrawerSection>
<EthWallet />
</DrawerSection>
<DrawerSection>
<VegaWallet />
</DrawerSection>
</div>
<DrawerNavLinks routes={routes} />
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
</>
);
};

View File

@ -1,91 +1,22 @@
import './nav.css';
import classNames from 'classnames'; import classNames from 'classnames';
import debounce from 'lodash/debounce'; import { NavLink, Link } from 'react-router-dom';
import React from 'react'; import { NetworkSwitcher } from '@vegaprotocol/environment';
import * as Dialog from '@radix-ui/react-dialog'; import type { HTMLAttributeAnchorTarget } from 'react';
import { useTranslation } from 'react-i18next'; import { useEffect, useState } from 'react';
import { Link, NavLink } from 'react-router-dom';
import vegaWhite from '../../images/vega_white.png';
import { Flags } from '../../config';
import {
AppStateActionType,
useAppState,
} from '../../contexts/app-state/app-state-context';
import Routes from '../../routes/routes'; import Routes from '../../routes/routes';
import { EthWallet } from '../eth-wallet'; import { useTranslation } from 'react-i18next';
import { VegaWallet } from '../vega-wallet'; import vegaWhite from '../../images/vega_white.png';
import debounce from 'lodash/debounce';
import { NavDrawer } from './nav-draw';
import {
getNavLinkClassNames,
Nav as ToolkitNav,
} from '@vegaprotocol/ui-toolkit';
const Fish = () => ( const useDebouncedResize = () => {
<svg const [windowWidth, setWindowWidth] = useState(window.innerWidth);
xmlns="http://www.w3.org/2000/svg"
width="33"
height="20"
viewBox="0 0 200 116"
fill="none"
data-testid="fairground-icon"
>
<g clipPath="url(#clip0)">
<path
d="M70.5918 32.8569L70.5918 22.7932L60.5254 22.7932L60.5254 32.8569L70.5918 32.8569Z"
fill="black"
/>
<path
d="M80.6641 83.2006L80.6641 73.1377L70.5977 73.1377L70.5977 83.2006L80.6641 83.2006Z"
fill="black"
/>
<path
d="M70.5918 93.2409L70.5918 83.1772L60.5254 83.1772L60.5254 93.2409L70.5918 93.2409Z"
fill="black"
/>
<path
d="M100.797 93.2636L100.797 73.1377L90.7305 73.1377L90.7305 93.2636L100.797 93.2636Z"
fill="black"
/>
<path
d="M90.7285 103.33L90.7285 93.2671L80.662 93.2671L80.662 103.33L90.7285 103.33Z"
fill="black"
/>
<path
d="M90.7285 22.8026L90.7285 12.74L80.662 12.74L80.662 22.8026L90.7285 22.8026Z"
fill="black"
/>
<path
d="M110.869 12.6108L110.869 2.54785L100.803 2.54785L100.803 12.6108L110.869 12.6108Z"
fill="black"
/>
<path
d="M120.934 103.326L120.934 73.1377L110.867 73.1377L110.867 103.326L120.934 103.326Z"
fill="black"
/>
<path
d="M110.869 113.384L110.869 103.321L100.803 103.321L100.803 113.384L110.869 113.384Z"
fill="black"
/>
<path
d="M161.328 52.9855L161.328 42.9226L151.262 42.9226L151.262 52.9855L161.328 52.9855Z"
fill="black"
/>
<path
d="M20.133 83.187L30.3354 83.187L30.3354 73.124L40.4017 73.124L40.4017 63.0613L50.4681 63.0613L50.4681 73.124L60.5345 73.124L60.5345 63.0613L70.6008 63.0613L80.6672 63.0613L131.135 63.0613L131.135 113.376L161.334 113.376L161.334 103.313L171.4 103.313L171.4 93.25L181.467 93.25L181.467 83.187L191.533 83.187L191.533 63.0613L181.467 63.0613L181.467 73.1241L171.4 73.1241L171.4 83.187L161.334 83.187L161.334 73.1241L171.4 73.1241L171.4 63.0613L161.334 63.0613L151.268 63.0613L141.201 63.0613L141.201 52.9983L141.201 32.8726L161.334 32.8726L171.4 32.8726L171.4 63.0613L181.467 63.0613L181.467 52.9983L191.533 52.9983L191.533 32.8726L181.467 32.8726L181.467 22.8096L171.4 22.8096L171.4 12.7467L161.334 12.7467L161.334 2.54785L141.201 2.54785L131.135 2.54785L131.135 52.9983L120.933 52.9983L120.933 12.7467L110.866 12.7467L110.866 52.9983L100.8 52.9983L100.8 22.8096L90.7336 22.8096L90.7336 52.9983L80.6672 52.9983L80.6672 32.8726L70.6008 32.8726L70.6008 52.9983L60.5345 52.9983L60.5345 42.9354L50.4681 42.9354L50.4681 52.9983L40.4017 52.9983L40.4017 42.9354L30.3354 42.9354L30.3354 32.8726L20.133 32.8726L20.133 22.8096L0.000308081 22.8096L0.000307201 42.9354L10.0666 42.9354L10.0666 52.9983L20.133 52.9983L20.133 63.0613L10.0666 63.0613L10.0666 73.124L0.000305881 73.124L0.000305002 93.25L20.133 93.25L20.133 83.187Z"
fill="black"
/>
</g>
<defs>
<clipPath id="clip0">
<rect width="200" height="116" fill="none" />
</clipPath>
</defs>
</svg>
);
export const Nav = () => { useEffect(() => {
const [windowWidth, setWindowWidth] = React.useState(window.innerWidth);
const isDesktop = windowWidth > 959;
const inverted = Flags.FAIRGROUND;
React.useEffect(() => {
const handleResizeDebounced = debounce(() => { const handleResizeDebounced = debounce(() => {
setWindowWidth(window.innerWidth); setWindowWidth(window.innerWidth);
}, 300); }, 300);
@ -96,167 +27,116 @@ export const Nav = () => {
window.removeEventListener('resize', handleResizeDebounced); window.removeEventListener('resize', handleResizeDebounced);
}; };
}, []); }, []);
return {
return ( windowWidth,
<div
className={`px-4 py-2 ${
inverted
? 'bg-clouds bg-no-repeat bg-cover bg-vega-yellow'
: 'border-neutral-700 border-b'
}`}
>
{isDesktop && <NavHeader fairground={inverted} />}
<div className="flex justify-between items-center mx-auto gap-12 lg:justify-start">
{!isDesktop && <NavHeader fairground={inverted} />}
<div className="flex gap-12 lg:flex-auto">
{isDesktop ? (
<NavLinks isDesktop={isDesktop} isInverted={inverted} />
) : (
<NavDrawer inverted={inverted} />
)}
</div>
</div>
</div>
);
};
const NavHeader = ({ fairground }: { fairground: boolean }) => {
const { t } = useTranslation();
return (
<div className="flex items-center gap-4">
<Link to="/">
{fairground ? (
<Fish />
) : (
<img alt="Vega" src={vegaWhite} height={30} width={30} />
)}
</Link>
<h1
data-testid="header-title"
className={`uppercase md:text-2xl sm:text-lg font-alpha uppercase calt my-0 ${
fairground ? 'text-black' : 'text-white'
}`}
>
{fairground ? t('fairgroundTitle') : t('title')}
</h1>
</div>
);
};
const DrawerSection = ({ children }: { children: React.ReactNode }) => (
<div className="px-4 my-4">{children}</div>
);
const IconLine = ({ inverted }: { inverted: boolean }) => (
<span className={`block w-6 h-[2px] ${inverted ? 'bg-black' : 'bg-white'}`} />
);
const NavDrawer = ({ inverted }: { inverted: boolean }) => {
const { appState, appDispatch } = useAppState();
const drawerContentClasses = classNames(
'drawer-content', // needed for css animation
// Positions the modal in the center of screen
'fixed w-[80vw] max-w-[420px] top-0 right-0',
'flex flex-col flex-nowrap justify-between h-full bg-banner overflow-y-scroll border-l border-white',
'bg-black text-neutral-200'
);
return (
<>
<button
onClick={() =>
appDispatch({
type: AppStateActionType.SET_DRAWER,
isOpen: true,
})
}
className="flex flex-col flex-nowrap gap-1"
>
<IconLine inverted={inverted} />
<IconLine inverted={inverted} />
<IconLine inverted={inverted} />
</button>
<Dialog.Root
open={appState.drawerOpen}
onOpenChange={(isOpen) =>
appDispatch({
type: AppStateActionType.SET_DRAWER,
isOpen,
})
}
>
<Dialog.Portal>
<Dialog.Overlay className="fixed inset-0 bg-white/15" />
<Dialog.Content className={drawerContentClasses}>
<div>
<DrawerSection>
<EthWallet />
</DrawerSection>
<DrawerSection>
<VegaWallet />
</DrawerSection>
</div>
<NavLinks isDesktop={false} />
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
</>
);
};
const NavLinks = ({
isDesktop,
isInverted,
}: {
isDesktop: boolean;
isInverted?: boolean;
}) => {
const { appDispatch } = useAppState();
const { t } = useTranslation();
const linkProps = {
onClick: () =>
appDispatch({ type: AppStateActionType.SET_DRAWER, isOpen: false }),
}; };
};
type NavbarTheme = 'inherit' | 'dark' | 'yellow';
interface NavbarProps {
navbarTheme?: NavbarTheme;
}
export const Nav = ({ navbarTheme = 'inherit' }: NavbarProps) => {
const { windowWidth } = useDebouncedResize();
const isDesktop = windowWidth > 995;
const { t } = useTranslation();
const isYellow = navbarTheme === 'yellow';
const routes = [ const routes = [
{ route: Routes.HOME, text: t('Home') }, {
{ route: Routes.VESTING, text: t('Vesting') }, name: t('Proposals'),
{ route: Routes.STAKING, text: t('Staking') }, path: Routes.PROPOSALS,
{ route: Routes.REWARDS, text: t('Rewards') }, },
{ route: Routes.WITHDRAWALS, text: t('Withdraw') }, {
{ route: Routes.GOVERNANCE, text: t('Governance') }, name: t('Validators'),
path: Routes.VALIDATORS,
},
{
name: t('Rewards'),
path: Routes.REWARDS,
},
{
name: t('Token'),
path: Routes.TOKEN,
},
{
name: t('Redeem'),
path: Routes.REDEEM,
},
{
name: t('Withdraw'),
path: Routes.WITHDRAWALS,
},
{
name: t('Supply & Vesting'),
path: Routes.TRANCHES,
},
]; ];
const navClasses = classNames('flex', {
'flex-row gap-2 mt-4 uppercase': isDesktop,
'flex-col': !isDesktop,
});
return ( return (
<nav className={navClasses}> <ToolkitNav
{routes.map(({ route, text }) => { navbarTheme={navbarTheme}
return ( icon={
<NavLink <Link to="/">
{...linkProps} <img alt="Vega" src={vegaWhite} height={30} width={30} />
to={route} </Link>
key={route} }
className={({ isActive }) => title={t('Governance')}
classNames({ titleContent={<NetworkSwitcher />}
'bg-vega-yellow text-black': !isInverted && isActive, >
'bg-transparent text-white hover:text-vega-yellow': {isDesktop ? (
!isInverted && !isActive, <nav className="flex items-center flex-1 px-2">
'bg-black text-white': isInverted && isActive, {routes.map((r) => (
'bg-transparent text-black hover:text-white': <AppNavLink {...r} navbarTheme={navbarTheme} />
isInverted && !isActive, ))}
'py-1 px-2': isDesktop, </nav>
'border-t border-white p-4': !isDesktop, ) : (
}) <nav className="flex items-center flex-1 px-2 justify-end">
} <NavDrawer inverted={isYellow} routes={routes} />
> </nav>
{text} )}
</NavLink> </ToolkitNav>
); );
})} };
</nav>
interface AppNavLinkProps {
name: string;
path: string;
navbarTheme: NavbarTheme;
testId?: string;
alignRight?: boolean;
target?: HTMLAttributeAnchorTarget;
}
const AppNavLink = ({
name,
path,
navbarTheme,
alignRight,
target,
testId = name,
}: AppNavLinkProps) => {
const borderClasses = classNames('absolute h-1 w-full bottom-[-1px] left-0', {
'bg-black dark:bg-vega-yellow': navbarTheme !== 'yellow',
'bg-black': navbarTheme === 'yellow',
});
return (
<NavLink
data-testid={testId}
to={{ pathname: path }}
className={getNavLinkClassNames(navbarTheme, alignRight)}
target={target}
end={true}
>
{({ isActive }) => {
return (
<>
{name}
{isActive && <span className={borderClasses} />}
</>
);
}}
</NavLink>
); );
}; };

View File

@ -1,3 +1,4 @@
import { Networks, useEnvironment } from '@vegaprotocol/environment';
import React from 'react'; import React from 'react';
import { Nav } from '../nav'; import { Nav } from '../nav';
@ -8,17 +9,20 @@ export interface TemplateSidebarProps {
} }
export function TemplateSidebar({ children, sidebar }: TemplateSidebarProps) { export function TemplateSidebar({ children, sidebar }: TemplateSidebarProps) {
const { VEGA_ENV } = useEnvironment();
return ( return (
<div className="w-full border-b border-neutral-700 lg:grid lg:grid-rows-[min-content_1fr] lg:grid-cols-[1fr_450px]"> <>
<Nav /> <Nav navbarTheme={VEGA_ENV === Networks.TESTNET ? 'yellow' : 'dark'} />
<main className="col-start-1 p-4">{children}</main> <div className="w-full border-b border-neutral-700 lg:grid lg:grid-rows-[min-content_1fr] lg:grid-cols-[1fr_450px]">
<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"> <main className="col-start-1 p-4">{children}</main>
{sidebar.map((Component, i) => ( <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">
<section className="mb-4 last:mb-0" key={i}> {sidebar.map((Component, i) => (
{Component} <section className="mb-4 last:mb-0" key={i}>
</section> {Component}
))} </section>
</aside> ))}
</div> </aside>
</div>
</>
); );
} }

View File

@ -22,20 +22,18 @@ export const VegaWalletContainer = ({ children }: VegaWalletContainerProps) => {
if (!pubKey) { if (!pubKey) {
return ( return (
<p> <Button
<Button data-testid="connect-to-vega-wallet-btn"
data-testid="connect-to-vega-wallet-btn" onClick={() => {
onClick={() => { appDispatch({
appDispatch({ type: AppStateActionType.SET_VEGA_WALLET_OVERLAY,
type: AppStateActionType.SET_VEGA_WALLET_OVERLAY, isOpen: true,
isOpen: true, });
}); openVegaWalletDialog();
openVegaWalletDialog(); }}
}} >
> {t('connectVegaWallet')}
{t('connectVegaWallet')} </Button>
</Button>
</p>
); );
} }

View File

@ -175,7 +175,7 @@ const VegaWalletConnected = ({ vegaKeys }: VegaWalletConnectedProps) => {
label={`${d.name || truncateMiddle(d.nodeId)} ${ label={`${d.name || truncateMiddle(d.nodeId)} ${
d.hasStakePending ? `(${t('thisEpoch')})` : '' d.hasStakePending ? `(${t('thisEpoch')})` : ''
}`} }`}
link={`${Routes.STAKING}/${d.nodeId}`} link={`${Routes.VALIDATORS}/${d.nodeId}`}
value={d.currentEpochStake} value={d.currentEpochStake}
/> />
</div> </div>
@ -186,7 +186,7 @@ const VegaWalletConnected = ({ vegaKeys }: VegaWalletConnectedProps) => {
label={`${d.name || truncateMiddle(d.nodeId)} (${t( label={`${d.name || truncateMiddle(d.nodeId)} (${t(
'nextEpoch' 'nextEpoch'
)})`} )})`}
link={`${Routes.STAKING}/${d.nodeId}`} link={`${Routes.VALIDATORS}/${d.nodeId}`}
value={d.nextEpochStake} value={d.nextEpochStake}
/> />
</div> </div>
@ -194,12 +194,12 @@ const VegaWalletConnected = ({ vegaKeys }: VegaWalletConnectedProps) => {
</div> </div>
))} ))}
<WalletCardActions> <WalletCardActions>
<Link className="flex-1" to={Routes.GOVERNANCE}> <Link className="flex-1" to={Routes.PROPOSALS}>
<Button size="sm" fill={true}> <Button size="sm" fill={true}>
{t('governance')} {t('governance')}
</Button> </Button>
</Link> </Link>
<Link className="flex-1" to={Routes.STAKING}> <Link className="flex-1" to={Routes.VALIDATORS}>
<Button size="sm" fill={true}> <Button size="sm" fill={true}>
{t('staking')} {t('staking')}
</Button> </Button>

View File

@ -472,7 +472,7 @@
"transaction": "Transaction", "transaction": "Transaction",
"associated": "Associated", "associated": "Associated",
"notAssociated": "Not Associated", "notAssociated": "Not Associated",
"title": "VEGA TOKEN", "title": "Governance",
"Use the Ethereum wallet you want to send your tokens to. You'll also need enough Ethereum to pay gas.": "Connect to the Ethereum wallet that holds your $VEGA tokens to see what can be redeemed from vesting tranches. To redeem tokens you will need some ETH to pay gas fees.\n", "Use the Ethereum wallet you want to send your tokens to. You'll also need enough Ethereum to pay gas.": "Connect to the Ethereum wallet that holds your $VEGA tokens to see what can be redeemed from vesting tranches. To redeem tokens you will need some ETH to pay gas fees.\n",
"Connect to see your stake": "Connect to see your stake", "Connect to see your stake": "Connect to see your stake",
"Staked on Vega validator": "Associated to Vega key", "Staked on Vega validator": "Associated to Vega key",
@ -715,5 +715,8 @@
"SpamProtectionMin": "Spam protection minimum", "SpamProtectionMin": "Spam protection minimum",
"ListAsset": "List Asset", "ListAsset": "List Asset",
"ListAssetDescription": "This asset needs to be listed on the collateral bridge before it can be used.", "ListAssetDescription": "This asset needs to be listed on the collateral bridge before it can be used.",
"ListAssetAction": "List asset" "ListAssetAction": "List asset",
"Proposals": "Proposals",
"Validators": "Validators",
"Redeem": "Redeem"
} }

View File

@ -56,7 +56,7 @@ export const Complete = ({
</Link> </Link>
</p> </p>
)} )}
<RouteLink to={Routes.VESTING}> <RouteLink to={Routes.REDEEM}>
<Button>{t('Check your vesting VEGA tokens')}</Button> <Button>{t('Check your vesting VEGA tokens')}</Button>
</RouteLink> </RouteLink>
</Callout> </Callout>

View File

@ -196,7 +196,7 @@ export const ProposalsListItemDetails = ({
)} )}
{proposal.id && ( {proposal.id && (
<div className="col-start-2 row-start-2 justify-self-end"> <div className="col-start-2 row-start-2 justify-self-end">
<Link to={`${Routes.GOVERNANCE}/${proposal.id}`}> <Link to={`${Routes.PROPOSALS}/${proposal.id}`}>
<Button data-testid="view-proposal-btn" size="sm"> <Button data-testid="view-proposal-btn" size="sm">
{t('View')} {t('View')}
</Button> </Button>

View File

@ -54,7 +54,7 @@ export const ProposalsList = ({ proposals }: ProposalsListProps) => {
<Link <Link
className="xs:justify-self-end" className="xs:justify-self-end"
data-testid="new-proposal-link" data-testid="new-proposal-link"
to={`${Routes.GOVERNANCE}/propose`} to={`${Routes.PROPOSALS}/propose`}
> >
<Button variant="primary" size="sm"> <Button variant="primary" size="sm">
{t('NewProposal')} {t('NewProposal')}

View File

@ -45,7 +45,7 @@ export const Propose = () => {
<p> <p>
<Link <Link
className="underline" className="underline"
to={`${Routes.GOVERNANCE}/propose/network-parameter`} to={`${Routes.PROPOSALS}/propose/network-parameter`}
> >
{t('NetworkParameter')} {t('NetworkParameter')}
</Link> </Link>
@ -55,7 +55,7 @@ export const Propose = () => {
<p> <p>
<Link <Link
className="underline" className="underline"
to={`${Routes.GOVERNANCE}/propose/new-market`} to={`${Routes.PROPOSALS}/propose/new-market`}
> >
{t('NewMarket')} {t('NewMarket')}
</Link> </Link>
@ -65,7 +65,7 @@ export const Propose = () => {
<p> <p>
<Link <Link
className="underline" className="underline"
to={`${Routes.GOVERNANCE}/propose/update-market`} to={`${Routes.PROPOSALS}/propose/update-market`}
> >
{t('UpdateMarket')} {t('UpdateMarket')}
</Link> </Link>
@ -75,7 +75,7 @@ export const Propose = () => {
<p> <p>
<Link <Link
className="underline" className="underline"
to={`${Routes.GOVERNANCE}/propose/new-asset`} to={`${Routes.PROPOSALS}/propose/new-asset`}
> >
{t('NewAsset')} {t('NewAsset')}
</Link> </Link>
@ -85,7 +85,7 @@ export const Propose = () => {
<p> <p>
<Link <Link
className="underline" className="underline"
to={`${Routes.GOVERNANCE}/propose/update-asset`} to={`${Routes.PROPOSALS}/propose/update-asset`}
> >
{t('UpdateAsset')} {t('UpdateAsset')}
</Link> </Link>
@ -95,7 +95,7 @@ export const Propose = () => {
<p> <p>
<Link <Link
className="underline" className="underline"
to={`${Routes.GOVERNANCE}/propose/freeform`} to={`${Routes.PROPOSALS}/propose/freeform`}
> >
{t('Freeform')} {t('Freeform')}
</Link> </Link>
@ -105,7 +105,7 @@ export const Propose = () => {
<p> <p>
<Link <Link
className="underline" className="underline"
to={`${Routes.GOVERNANCE}/propose/raw`} to={`${Routes.PROPOSALS}/propose/raw`}
> >
{t('RawProposal')} {t('RawProposal')}
</Link> </Link>

View File

@ -57,7 +57,7 @@ const Home = ({ name }: RouteChildProps) => {
'Once unlocked they can be redeemed from the contract so that you can transfer them between wallets.' 'Once unlocked they can be redeemed from the contract so that you can transfer them between wallets.'
)} )}
</p> </p>
<Link to={Routes.VESTING}> <Link to={Routes.REDEEM}>
<Button <Button
variant="primary" variant="primary"
size="md" size="md"
@ -93,7 +93,7 @@ const Home = ({ name }: RouteChildProps) => {
<p> <p>
<Link <Link
data-testid="associate-vega-tokens-link-on-homepage" data-testid="associate-vega-tokens-link-on-homepage"
to={`${Routes.STAKING}/associate`} to={`${Routes.VALIDATORS}/associate`}
className="underline text-white" className="underline text-white"
> >
{t('Associate VEGA tokens')} {t('Associate VEGA tokens')}
@ -110,7 +110,7 @@ const Home = ({ name }: RouteChildProps) => {
)} )}
</p> </p>
<p> <p>
<Link to={Routes.STAKING}> <Link to={Routes.VALIDATORS}>
<Button size="md" data-testid="staking-button-on-homepage"> <Button size="md" data-testid="staking-button-on-homepage">
{t('Nominate a validator')} {t('Nominate a validator')}
</Button> </Button>
@ -127,7 +127,7 @@ const Home = ({ name }: RouteChildProps) => {
)} )}
</p> </p>
<p> <p>
<Link to={Routes.GOVERNANCE}> <Link to={Routes.PROPOSALS}>
<Button size="md" data-testid="governance-button-on-homepage"> <Button size="md" data-testid="governance-button-on-homepage">
{t('View Governance proposals')} {t('View Governance proposals')}
</Button> </Button>

View File

@ -10,7 +10,7 @@ import Routes from '../routes';
const RedemptionIndex = ({ name }: RouteChildProps) => { const RedemptionIndex = ({ name }: RouteChildProps) => {
useDocumentTitle(name); useDocumentTitle(name);
const { t } = useTranslation(); const { t } = useTranslation();
const tranche = useMatch(`${Routes.VESTING}/:id`); const tranche = useMatch(`${Routes.REDEEM}/:id`);
return ( return (
<> <>

View File

@ -121,13 +121,13 @@ export const RedeemFromTranche = () => {
stakingLink: ( stakingLink: (
<Link <Link
className="underline text-white" className="underline text-white"
to={Routes.STAKING} to={Routes.VALIDATORS}
/> />
), ),
governanceLink: ( governanceLink: (
<Link <Link
className="underline text-white" className="underline text-white"
to={Routes.GOVERNANCE} to={Routes.PROPOSALS}
/> />
), ),
}} }}

View File

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { Outlet } from 'react-router-dom'; import { Navigate, Outlet } from 'react-router-dom';
import Home from './home'; import Home from './home';
import NotFound from './not-found'; import NotFound from './not-found';
import NotPermitted from './not-permitted'; import NotPermitted from './not-permitted';
@ -193,61 +193,32 @@ const LazyWithdrawals = React.lazy(
) )
); );
const routerConfig = [ const redirects = [
{ {
path: Routes.HOME, path: Routes.HOME,
// Not lazy as loaded when a user first hits the site element: <Navigate to={Routes.PROPOSALS} replace />,
element: <Home name="Home" />,
},
{
path: Routes.TRANCHES,
element: <LazyTranches name="Tranches" />,
children: [
{ index: true, element: <LazyTranchesTranches /> },
{ path: ':trancheId', element: <LazyTranchesTranche /> },
],
},
{
path: Routes.CLAIM,
element: <LazyClaim name="Claim" />,
}, },
{ {
path: Routes.STAKING, path: Routes.STAKING,
element: <LazyStaking name="Staking" />, element: <Navigate to={Routes.VALIDATORS} replace />,
children: [
{ path: 'associate', element: <LazyStakingAssociate /> },
{ path: 'disassociate', element: <LazyStakingDisassociate /> },
{ path: ':node', element: <LazyStakingNode /> },
{
index: true,
element: <LazyStakingIndex />,
},
],
}, },
{ {
path: Routes.REWARDS, path: '/tranches',
element: <LazyRewards name="Rewards" />, element: <Navigate to={Routes.TRANCHES} replace />,
}, },
{ {
path: Routes.WITHDRAWALS, path: '/withdrawals',
element: <LazyWithdrawals name="Withdrawals" />, element: <Navigate to={Routes.WITHDRAWALS} replace />,
}, },
{ {
path: Routes.VESTING, path: '/vesting',
element: <LazyRedemption name="Vesting" />, element: <Navigate to={Routes.REDEEM} replace />,
children: [
{
index: true,
element: <LazyRedemptionIndex />,
},
{
path: ':id',
element: <LazyRedemptionTranche />,
},
],
}, },
];
const routerConfig = [
{ {
path: Routes.GOVERNANCE, path: Routes.PROPOSALS,
element: <LazyGovernance name="Governance" />, element: <LazyGovernance name="Governance" />,
children: [ children: [
{ index: true, element: <LazyGovernanceProposals /> }, { index: true, element: <LazyGovernanceProposals /> },
@ -281,6 +252,63 @@ const routerConfig = [
{ path: 'rejected', element: <LazyRejectedGovernanceProposals /> }, { path: 'rejected', element: <LazyRejectedGovernanceProposals /> },
], ],
}, },
{
path: Routes.VALIDATORS,
element: <LazyStaking name="Staking" />,
children: [
{ path: 'associate', element: <LazyStakingAssociate /> },
{ path: 'disassociate', element: <LazyStakingDisassociate /> },
{ path: ':node', element: <LazyStakingNode /> },
{
index: true,
element: <LazyStakingIndex />,
},
],
},
{
path: Routes.REWARDS,
element: <LazyRewards name="Rewards" />,
},
{
path: Routes.TOKEN,
// Not lazy as loaded when a user first hits the site
children: [
{
element: <Home name="Home" />,
index: true,
},
{
path: Routes.TRANCHES,
element: <LazyTranches name="Tranches" />,
children: [
{ index: true, element: <LazyTranchesTranches /> },
{ path: ':trancheId', element: <LazyTranchesTranche /> },
],
},
{
path: Routes.WITHDRAWALS,
element: <LazyWithdrawals name="Withdrawals" />,
},
{
path: Routes.REDEEM,
element: <LazyRedemption name="Vesting" />,
children: [
{
index: true,
element: <LazyRedemptionIndex />,
},
{
path: ':id',
element: <LazyRedemptionTranche />,
},
],
},
],
},
{
path: Routes.CLAIM,
element: <LazyClaim name="Claim" />,
},
{ {
path: Routes.NOT_PERMITTED, path: Routes.NOT_PERMITTED,
// Not lazy as loaded when a user first hits the site // Not lazy as loaded when a user first hits the site
@ -295,6 +323,7 @@ const routerConfig = [
// Not lazy as loaded when a user first hits the site // Not lazy as loaded when a user first hits the site
element: <NotFound name="NotFound" />, element: <NotFound name="NotFound" />,
}, },
...redirects,
]; ];
export default routerConfig; export default routerConfig;

View File

@ -1,13 +1,16 @@
export default { export default {
HOME: '/', HOME: '/',
TRANCHES: '/tranches',
CLAIM: '/claim', CLAIM: '/claim',
STAKING: '/staking', VALIDATORS: '/validators',
REWARDS: '/rewards', REWARDS: '/rewards',
WITHDRAWALS: '/withdrawals', PROPOSALS: '/proposals',
GOVERNANCE: '/governance',
VESTING: '/vesting',
NOT_PERMITTED: '/not-permitted', NOT_PERMITTED: '/not-permitted',
NOT_FOUND: '/not-found', NOT_FOUND: '/not-found',
CONTRACTS: '/contracts', CONTRACTS: '/contracts',
STAKING: '/staking',
TOKEN: '/token',
REDEEM: '/token/redeem',
WITHDRAWALS: '/token/withdraw',
TRANCHES: '/token/tranches',
ASSOCIATE: '/token/associate',
}; };

View File

@ -93,7 +93,7 @@ export const AssociateTransaction = ({
vegaKey: truncateMiddle(vegaKey), vegaKey: truncateMiddle(vegaKey),
})} })}
completeFooter={ completeFooter={
<RouteLink to={Routes.STAKING}> <RouteLink to={Routes.VALIDATORS}>
<Button>{t('Nominate Stake to Validator Node')}</Button> <Button>{t('Nominate Stake to Validator Node')}</Button>
</RouteLink> </RouteLink>
} }

View File

@ -39,7 +39,7 @@ export const DisassociateTransaction = ({
}) })
} }
completeFooter={ completeFooter={
<Link to={Routes.STAKING}> <Link to={Routes.VALIDATORS}>
<Button>{t('backToStaking')}</Button> <Button>{t('backToStaking')}</Button>
</Link> </Link>
} }

View File

@ -8,6 +8,7 @@ import {
} from '@vegaprotocol/ui-toolkit'; } from '@vegaprotocol/ui-toolkit';
import { createDocsLinks, ExternalLinks } from '@vegaprotocol/react-helpers'; import { createDocsLinks, ExternalLinks } from '@vegaprotocol/react-helpers';
import { useEnvironment } from '@vegaprotocol/environment'; import { useEnvironment } from '@vegaprotocol/environment';
import Routes from '../../../routes/routes';
export const StakingIntro = () => { export const StakingIntro = () => {
const { t } = useTranslation(); const { t } = useTranslation();
@ -24,7 +25,7 @@ export const StakingIntro = () => {
<li> <li>
{t('stakingDescription1')}{' '} {t('stakingDescription1')}{' '}
<Link <Link
to="/staking/associate" to={Routes.ASSOCIATE}
className="underline" className="underline"
data-testid="staking-associate-link" data-testid="staking-associate-link"
> >

View File

@ -9,8 +9,8 @@ import type { RouteChildProps } from '..';
const StakingRouter = ({ name }: RouteChildProps) => { const StakingRouter = ({ name }: RouteChildProps) => {
useDocumentTitle(name); useDocumentTitle(name);
const { t } = useTranslation(); const { t } = useTranslation();
const associate = useMatch('/staking/associate'); const associate = useMatch('/validators/associate');
const disassociate = useMatch('/staking/disassociate'); const disassociate = useMatch('/validators/disassociate');
const title = React.useMemo(() => { const title = React.useMemo(() => {
if (associate) { if (associate) {

View File

@ -44,7 +44,7 @@ export const StakeSuccess = ({
<div> <div>
<p>{message}</p> <p>{message}</p>
<p> <p>
<Link className="underline" to={Routes.STAKING}> <Link className="underline" to={Routes.VALIDATORS}>
{t('backToStaking')} {t('backToStaking')}
</Link> </Link>
</p> </p>

View File

@ -88,7 +88,7 @@ export const VestingChart = () => {
stroke={colors.white} stroke={colors.white}
strokeWidth={2} strokeWidth={2}
label={{ label={{
position: 'right', position: 'left',
value: currentDate, value: currentDate,
fill: colors.white, fill: colors.white,
}} }}

View File

@ -7,8 +7,12 @@ import { VegaWalletConnectButton } from '../vega-wallet-connect-button';
import { ThemeSwitcher } from '@vegaprotocol/ui-toolkit'; import { ThemeSwitcher } from '@vegaprotocol/ui-toolkit';
import { Vega } from '../icons/vega'; import { Vega } from '../icons/vega';
import type { HTMLAttributeAnchorTarget } from 'react'; import type { HTMLAttributeAnchorTarget } from 'react';
import testnetBg from '../../assets/green-cloud.png';
import { Routes } from '../../pages/client-router'; import { Routes } from '../../pages/client-router';
import {
getNavLinkClassNames,
getActiveNavLinkClassNames,
Nav,
} from '@vegaprotocol/ui-toolkit';
type NavbarTheme = 'inherit' | 'dark' | 'yellow'; type NavbarTheme = 'inherit' | 'dark' | 'yellow';
interface NavbarProps { interface NavbarProps {
@ -27,61 +31,40 @@ export const Navbar = ({
marketId: store.marketId, marketId: store.marketId,
})); }));
const tradingPath = marketId ? `/markets/${marketId}` : '/markets'; const tradingPath = marketId ? `/markets/${marketId}` : '/markets';
const themeWrapperClasses = classNames({
dark: navbarTheme === 'dark',
});
const isYellow = navbarTheme === 'yellow';
const navbarClasses = classNames(
'flex items-stretch border-b px-4 border-default',
{
'dark:bg-black dark:text-white': !isYellow,
'bg-vega-yellow text-black bg-right-top bg-no-repeat bg-contain':
isYellow,
}
);
return ( return (
<div className={themeWrapperClasses}> <Nav
<div navbarTheme={navbarTheme}
className={navbarClasses} title={t('Console')}
style={{ titleContent={<NetworkSwitcher />}
backgroundImage: isYellow ? `url("${testnetBg.src}")` : '', icon={
}} <Link to="/">
> <Vega className="w-13" />
<div className="flex gap-4 items-center"> </Link>
<Link to="/"> }
<Vega className="w-13" /> >
</Link> <AppNavLink
<NetworkSwitcher /> name={t('Trading')}
</div> path={tradingPath}
<nav className="flex items-center flex-1 px-2"> navbarTheme={navbarTheme}
<AppNavLink />
name={t('Trading')} <AppNavLink
path={tradingPath} name={t('Portfolio')}
navbarTheme={navbarTheme} path={Routes.PORTFOLIO}
/> navbarTheme={navbarTheme}
<AppNavLink />
name={t('Portfolio')} <div className="flex items-center gap-2 ml-auto">
path={Routes.PORTFOLIO} <a
navbarTheme={navbarTheme} href={`${VEGA_TOKEN_URL}/governance`}
/> target="_blank"
<a rel="noreferrer"
href={`${VEGA_TOKEN_URL}/governance`} className={getActiveNavLinkClassNames(false, navbarTheme, true)}
target="_blank" >
rel="noreferrer" {t('Governance')}
className={getActiveNavLinkClassNames(false, navbarTheme, true)} </a>
> <VegaWalletConnectButton />
{t('Governance')} <ThemeSwitcher theme={theme} onToggle={toggleTheme} />
</a>
</nav>
<div className="flex items-center gap-2 ml-auto">
<VegaWalletConnectButton />
<ThemeSwitcher theme={theme} onToggle={toggleTheme} />
</div>
</div> </div>
</div> </Nav>
); );
}; };
@ -124,28 +107,3 @@ const AppNavLink = ({
</NavLink> </NavLink>
); );
}; };
function getNavLinkClassNames(
navbarTheme: string,
alignRight = false
): (props: { isActive?: boolean }) => string | undefined {
return ({ isActive = false }) => {
return getActiveNavLinkClassNames(isActive, navbarTheme, alignRight);
};
}
const getActiveNavLinkClassNames = (
isActive: boolean,
navbarTheme: string,
alignRight = false
): string | undefined => {
return classNames('mx-2 py-3 self-end relative', {
'cursor-default': isActive,
'text-black dark:text-white': isActive && navbarTheme !== 'yellow',
'text-neutral-500 dark:text-neutral-400 hover:text-black dark:hover:text-neutral-300':
!isActive && navbarTheme !== 'yellow',
'ml-auto': alignRight,
'text-black': isActive && navbarTheme === 'yellow',
'text-black/60 hover:text-black': !isActive && navbarTheme === 'yellow',
});
};

View File

@ -18,6 +18,7 @@ export * from './key-value-table';
export * from './link'; export * from './link';
export * from './loader'; export * from './loader';
export * from './lozenge'; export * from './lozenge';
export * from './nav';
export * from './popover'; export * from './popover';
export * from './price-change'; export * from './price-change';
export * from './progress-bar'; export * from './progress-bar';

View File

@ -0,0 +1 @@
export * from './nav';

View File

@ -0,0 +1,19 @@
import { render } from '@testing-library/react';
import { Nav } from './nav';
describe('Nav', () => {
it('should render title, title content, icon and children', () => {
const { baseElement } = render(
<Nav
navbarTheme="dark"
icon={<div data-testid="icon" />}
titleContent={<div data-testid="title-content" />}
title={'Some title'}
>
<div data-testid="link"></div>
</Nav>
);
expect(baseElement).toBeTruthy();
});
});

View File

@ -0,0 +1,34 @@
import type { Meta, Story } from '@storybook/react';
import { ButtonLink } from '../button';
import { VegaLogo } from '../vega-logo';
import { Nav } from './nav';
export default {
component: Nav,
title: 'Nav',
} as Meta;
const Template: Story = ({ icon, title, titleContent }) => (
<div className="flex w-full">
<Nav icon={icon} title={title} titleContent={titleContent}>
<div className="mx-4">
<ButtonLink>Some link</ButtonLink>{' '}
</div>
<div className="mx-4">
<ButtonLink>Some other link</ButtonLink>
</div>
</Nav>
</div>
);
const props = {
icon: <VegaLogo />,
title: 'Title',
titleContent: <div>Content next to title</div>,
};
export const Default = Template.bind({});
Default.args = {
...props,
};

View File

@ -0,0 +1,79 @@
import classNames from 'classnames';
import type { ReactNode } from 'react';
export function getNavLinkClassNames(
navbarTheme: string,
alignRight = false
): (props: { isActive?: boolean }) => string | undefined {
return ({ isActive = false }) => {
return getActiveNavLinkClassNames(isActive, navbarTheme, alignRight);
};
}
export const getActiveNavLinkClassNames = (
isActive: boolean,
navbarTheme: string,
alignRight = false
): string | undefined => {
return classNames('mx-2 py-3 self-end relative', {
'cursor-default': isActive,
'text-black dark:text-white': isActive && navbarTheme !== 'yellow',
'text-neutral-500 dark:text-neutral-400 hover:text-black dark:hover:text-neutral-300':
!isActive && navbarTheme !== 'yellow',
'ml-auto': alignRight,
'text-black': isActive && navbarTheme === 'yellow',
'text-black/60 hover:text-black': !isActive && navbarTheme === 'yellow',
});
};
type NavbarTheme = 'inherit' | 'dark' | 'yellow';
interface NavbarProps {
navbarTheme?: NavbarTheme;
icon: ReactNode;
titleContent: ReactNode;
children: ReactNode;
title: string;
}
export const Nav = ({
navbarTheme = 'inherit',
children,
icon,
titleContent,
title,
}: NavbarProps) => {
const themeWrapperClasses = classNames('w-full', {
dark: navbarTheme === 'dark',
});
const isYellow = navbarTheme === 'yellow';
const navbarClasses = classNames(
'flex items-stretch border-b px-4 border-default',
{
'dark:bg-black dark:text-white': !isYellow,
'bg-vega-yellow text-black bg-right-top bg-no-repeat bg-contain':
isYellow,
}
);
return (
<div className={themeWrapperClasses}>
<div className={navbarClasses}>
<div className="flex gap-4 items-center">
{icon}
<h1
className={classNames(
'h-full flex flex-col my-0 justify-center font-alpha calt uppercase',
{ 'text-black': isYellow, 'text-white': !isYellow }
)}
>
{title}
</h1>
{titleContent}
</div>
{children}
</div>
</div>
);
};