feat(ui): change sidebar label to tooltip hover and supported multi language (#12996)

* change sidebar label to tooplip hover and supported multi language

* fix alignment and cypress issues

* fix cpress issue
This commit is contained in:
Ashish Gupta 2023-08-29 13:01:06 +05:30 committed by GitHub
parent 3cc15e6d0b
commit 197dd196fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 343 additions and 212 deletions

View File

@ -171,7 +171,7 @@ export const goToAdvanceSearch = () => {
'explorePage' 'explorePage'
); );
// Navigate to explore page // Navigate to explore page
cy.get('[data-testid="appbar-item-explore"]') cy.get('[data-testid="app-bar-item-explore"]')
.should('exist') .should('exist')
.and('be.visible') .and('be.visible')
.click(); .click();

View File

@ -376,7 +376,7 @@ export const deleteCreatedService = (
'api/v1/teams/name/Organization?fields=*', 'api/v1/teams/name/Organization?fields=*',
'getSettingsPage' 'getSettingsPage'
); );
cy.get('[data-testid="appbar-item-settings"]') cy.get('[data-testid="app-bar-item-settings"]')
.should('be.visible') .should('be.visible')
.click({ force: true }); .click({ force: true });
verifyResponseStatusCode('@getSettingsPage', 200); verifyResponseStatusCode('@getSettingsPage', 200);
@ -454,7 +454,7 @@ export const goToAddNewServicePage = (service_type) => {
'getSettingsPage' 'getSettingsPage'
); );
// Click on settings page // Click on settings page
cy.get('[data-testid="appbar-item-settings"]').should('be.visible').click(); cy.get('[data-testid="app-bar-item-settings"]').should('be.visible').click();
verifyResponseStatusCode('@getSettingsPage', 200); verifyResponseStatusCode('@getSettingsPage', 200);
// Services page // Services page
interceptURL('GET', '/api/v1/services/*', 'getServiceList'); interceptURL('GET', '/api/v1/services/*', 'getServiceList');
@ -1055,7 +1055,7 @@ export const updateDescriptionForIngestedTables = (
verifyResponseStatusCode('@updateEntity', 200); verifyResponseStatusCode('@updateEntity', 200);
// re-run ingestion flow // re-run ingestion flow
cy.get('[data-testid="appbar-item-settings"]').should('be.visible').click(); cy.get('[data-testid="app-bar-item-settings"]').should('be.visible').click();
// Services page // Services page
cy.get('.ant-menu-title-content').contains(type).should('be.visible').click(); cy.get('.ant-menu-title-content').contains(type).should('be.visible').click();
@ -1254,7 +1254,7 @@ export const visitServiceDetailsPage = (
export const visitDataModelPage = (dataModelFQN, dataModelName) => { export const visitDataModelPage = (dataModelFQN, dataModelName) => {
interceptURL('GET', '/api/v1/teams/name/*', 'getOrganization'); interceptURL('GET', '/api/v1/teams/name/*', 'getOrganization');
cy.get('[data-testid="appbar-item-settings"]').click(); cy.get('[data-testid="app-bar-item-settings"]').click();
verifyResponseStatusCode('@getOrganization', 200); verifyResponseStatusCode('@getOrganization', 200);

View File

@ -19,7 +19,7 @@ export const visitServiceDetailsPage = (service, verifyHeader = true) => {
'api/v1/teams/name/Organization?fields=*', 'api/v1/teams/name/Organization?fields=*',
'getSettingsPage' 'getSettingsPage'
); );
cy.get('[data-testid="appbar-item-settings"]').should('be.visible').click(); cy.get('[data-testid="app-bar-item-settings"]').should('be.visible').click();
verifyResponseStatusCode('@getSettingsPage', 200); verifyResponseStatusCode('@getSettingsPage', 200);
// Services page // Services page

View File

@ -62,29 +62,29 @@ export const LEFT_PANEL_DETAILS = {
export const NAVBAR_DETAILS = { export const NAVBAR_DETAILS = {
explore: { explore: {
testid: '[data-testid="appbar-item-explore"]', testid: '[data-testid="app-bar-item-explore"]',
url: `${BASE_URL}/explore/tables`, url: `${BASE_URL}/explore/tables`,
}, },
quality: { quality: {
testid: '[data-testid="appbar-item-data-quality"]', testid: '[data-testid="app-bar-item-data-quality"]',
url: `${BASE_URL}/data-quality`, url: `${BASE_URL}/data-quality`,
}, },
insights: { insights: {
testid: '[data-testid="appbar-item-data-insight"]', testid: '[data-testid="app-bar-item-data-insight"]',
url: `${BASE_URL}/data-insights`, url: `${BASE_URL}/data-insights`,
}, },
glossary: { glossary: {
testid: `[data-testid="governance"]`, testid: `[data-testid="governance"]`,
subMenu: `[data-testid="appbar-item-glossary"]`, subMenu: `[data-testid="app-bar-item-glossary"]`,
url: `${BASE_URL}/glossary`, url: `${BASE_URL}/glossary`,
}, },
tags: { tags: {
testid: `[data-testid="governance"]`, testid: `[data-testid="governance"]`,
subMenu: '[data-testid="appbar-item-tags"]', subMenu: '[data-testid="app-bar-item-tags"]',
url: `${BASE_URL}/tags/`, url: `${BASE_URL}/tags/`,
}, },
settings: { settings: {
testid: '[data-testid="appbar-item-settings"]', testid: '[data-testid="app-bar-item-settings"]',
url: `${BASE_URL}/settings/members/teams/Organization`, url: `${BASE_URL}/settings/members/teams/Organization`,
}, },
profile: { profile: {
@ -96,7 +96,7 @@ export const NAVBAR_DETAILS = {
export const SETTINGS_LEFT_PANEL = { export const SETTINGS_LEFT_PANEL = {
settings: { settings: {
testid: '[data-testid="appbar-item-settings"]', testid: '[data-testid="app-bar-item-settings"]',
url: `${BASE_URL}/settings/members/teams/Organization`, url: `${BASE_URL}/settings/members/teams/Organization`,
}, },
teams: { teams: {

View File

@ -102,7 +102,7 @@ describe('Postgres Ingestion', () => {
'/api/v1/services/ingestionPipelines/deploy/*', '/api/v1/services/ingestionPipelines/deploy/*',
'deployIngestion' 'deployIngestion'
); );
cy.get('[data-testid="appbar-item-settings"]') cy.get('[data-testid="app-bar-item-settings"]')
.should('be.visible') .should('be.visible')
.click({ force: true }); .click({ force: true });
verifyResponseStatusCode('@getSettingsPage', 200); verifyResponseStatusCode('@getSettingsPage', 200);

View File

@ -101,7 +101,7 @@ describe('RedShift Ingestion', () => {
'/api/v1/services/ingestionPipelines/*/pipelineStatus?startTs=*&endTs=*', '/api/v1/services/ingestionPipelines/*/pipelineStatus?startTs=*&endTs=*',
'pipelineStatus' 'pipelineStatus'
); );
cy.get('[data-testid="appbar-item-settings"]') cy.get('[data-testid="app-bar-item-settings"]')
.should('be.visible') .should('be.visible')
.click({ force: true }); .click({ force: true });
verifyResponseStatusCode('@getSettingsPage', 200); verifyResponseStatusCode('@getSettingsPage', 200);
@ -219,7 +219,7 @@ describe('RedShift Ingestion', () => {
.should('be.visible') .should('be.visible')
.click(); .click();
cy.get('[data-testid="appbar-item-tags"]') cy.get('[data-testid="app-bar-item-tags"]')
.should('exist') .should('exist')
.should('be.visible') .should('be.visible')
.click({ waitForAnimations: true }); .click({ waitForAnimations: true });

View File

@ -70,7 +70,7 @@ describe('Restore entity functionality should work properly', () => {
}); });
it('Check Soft Deleted entity table', () => { it('Check Soft Deleted entity table', () => {
cy.get('[data-testid="appbar-item-explore"]').click(); cy.get('[data-testid="app-bar-item-explore"]').click();
verifyResponseStatusCode('@nonDeletedTables', 200); verifyResponseStatusCode('@nonDeletedTables', 200);
cy.get('[data-testid="show-deleted"]').should('exist').click(); cy.get('[data-testid="show-deleted"]').should('exist').click();
@ -91,7 +91,7 @@ describe('Restore entity functionality should work properly', () => {
}); });
it("Check Soft Deleted table in it's Schema", () => { it("Check Soft Deleted table in it's Schema", () => {
cy.get('[data-testid="appbar-item-explore"]').click(); cy.get('[data-testid="app-bar-item-explore"]').click();
verifyResponseStatusCode('@nonDeletedTables', 200); verifyResponseStatusCode('@nonDeletedTables', 200);
cy.get('[data-testid="show-deleted"]').click(); cy.get('[data-testid="show-deleted"]').click();
verifyResponseStatusCode('@showDeletedTables', 200); verifyResponseStatusCode('@showDeletedTables', 200);
@ -133,7 +133,7 @@ describe('Restore entity functionality should work properly', () => {
}); });
it('Restore Soft Deleted table', () => { it('Restore Soft Deleted table', () => {
cy.get('[data-testid="appbar-item-explore"]').click(); cy.get('[data-testid="app-bar-item-explore"]').click();
verifyResponseStatusCode('@nonDeletedTables', 200); verifyResponseStatusCode('@nonDeletedTables', 200);
cy.get('[data-testid="show-deleted"]').click(); cy.get('[data-testid="show-deleted"]').click();
verifyResponseStatusCode('@showDeletedTables', 200); verifyResponseStatusCode('@showDeletedTables', 200);

View File

@ -38,7 +38,9 @@ describe('Add nested teams and test TeamsSelectable', () => {
beforeEach(() => { beforeEach(() => {
cy.login(); cy.login();
cy.get('[data-testid="appbar-item-settings"]').should('be.visible').click(); cy.get('[data-testid="app-bar-item-settings"]')
.should('be.visible')
.click();
interceptURL('GET', '/api/v1/teams/name/*', 'getOrganization'); interceptURL('GET', '/api/v1/teams/name/*', 'getOrganization');
interceptURL('GET', '/api/v1/permissions/team/name/*', 'getPermissions'); interceptURL('GET', '/api/v1/permissions/team/name/*', 'getPermissions');
// Clicking on teams // Clicking on teams

View File

@ -163,7 +163,7 @@ describe('Add and Remove Owner', () => {
'testSuiteDetails' 'testSuiteDetails'
); );
interceptURL('GET', '/api/v1/dataQuality/testCases?*', 'testCases'); interceptURL('GET', '/api/v1/dataQuality/testCases?*', 'testCases');
cy.get('[data-testid="appbar-item-data-quality"]') cy.get('[data-testid="app-bar-item-data-quality"]')
.should('be.visible') .should('be.visible')
.click(); .click();
verifyResponseStatusCode('@testSuites', 200); verifyResponseStatusCode('@testSuites', 200);
@ -189,7 +189,9 @@ describe('Add and Remove Owner', () => {
'/api/v1/teams/name/Organization?fields=*', '/api/v1/teams/name/Organization?fields=*',
'getOrganization' 'getOrganization'
); );
cy.get('[data-testid="appbar-item-settings"]').should('be.visible').click(); cy.get('[data-testid="app-bar-item-settings"]')
.should('be.visible')
.click();
verifyResponseStatusCode('@entityPermission', 200); verifyResponseStatusCode('@entityPermission', 200);
verifyResponseStatusCode('@getOrganization', 200); verifyResponseStatusCode('@getOrganization', 200);
verifyResponseStatusCode('@teamPermission', 200); verifyResponseStatusCode('@teamPermission', 200);
@ -203,8 +205,9 @@ describe('Add and Remove Owner', () => {
interceptURL('GET', '/api/v1/permissions/glossary/*', 'glossaryPermission'); interceptURL('GET', '/api/v1/permissions/glossary/*', 'glossaryPermission');
interceptURL('GET', '/api/v1/glossaries?*', 'getGlossaries'); interceptURL('GET', '/api/v1/glossaries?*', 'getGlossaries');
cy.get('[data-testid="governance"]').should('be.visible').click(); cy.get('[data-testid="governance"]').should('be.visible').click();
cy.get('[data-testid="appbar-item-glossary"]').click({ cy.get('[data-testid="app-bar-item-glossary"]').click({
waitForAnimations: true, waitForAnimations: true,
force: true,
}); });
verifyResponseStatusCode('@getGlossaries', 200); verifyResponseStatusCode('@getGlossaries', 200);
cy.get('[data-testid="add-glossary"]').click(); cy.get('[data-testid="add-glossary"]').click();
@ -238,9 +241,9 @@ describe('Add and Remove Owner', () => {
'getGlossaryTermDetails' 'getGlossaryTermDetails'
); );
cy.get('[data-testid="governance"]').should('be.visible').click(); cy.get('[data-testid="governance"]').should('be.visible').click();
cy.get('[data-testid="appbar-item-glossary"]') cy.get('[data-testid="app-bar-item-glossary"]')
.should('be.visible') .should('be.visible')
.click({ waitForAnimations: true }); .click({ waitForAnimations: true, force: true });
verifyResponseStatusCode('@getGlossaries', 200); verifyResponseStatusCode('@getGlossaries', 200);
verifyResponseStatusCode('@glossaryPermission', 200); verifyResponseStatusCode('@glossaryPermission', 200);
interceptURL('GET', '/api/v1/glossaryTerms*', 'getGlossaryTerms'); interceptURL('GET', '/api/v1/glossaryTerms*', 'getGlossaryTerms');
@ -274,9 +277,9 @@ describe('Add and Remove Owner', () => {
interceptURL('GET', '/api/v1/glossaries?*', 'getGlossaries'); interceptURL('GET', '/api/v1/glossaries?*', 'getGlossaries');
cy.get('[data-testid="governance"]').should('be.visible').click(); cy.get('[data-testid="governance"]').should('be.visible').click();
cy.get('[data-testid="appbar-item-glossary"]') cy.get('[data-testid="app-bar-item-glossary"]')
.should('be.visible') .should('be.visible')
.click({ waitForAnimations: true }); .click({ waitForAnimations: true, force: true });
verifyResponseStatusCode('@getGlossaries', 200); verifyResponseStatusCode('@getGlossaries', 200);
verifyResponseStatusCode('@glossaryPermission', 200); verifyResponseStatusCode('@glossaryPermission', 200);
interceptURL('GET', '/api/v1/glossaryTerms*', 'getGlossaryTerms'); interceptURL('GET', '/api/v1/glossaryTerms*', 'getGlossaryTerms');

View File

@ -28,7 +28,9 @@ describe('Test Add role and assign it to the user', () => {
cy.login(); cy.login();
interceptURL('GET', '*api/v1/roles*', 'getRoles'); interceptURL('GET', '*api/v1/roles*', 'getRoles');
interceptURL('GET', '/api/v1/users?*', 'usersPage'); interceptURL('GET', '/api/v1/users?*', 'usersPage');
cy.get('[data-testid="appbar-item-settings"]').should('be.visible').click(); cy.get('[data-testid="app-bar-item-settings"]')
.should('be.visible')
.click();
}); });
it('Create role', () => { it('Create role', () => {

View File

@ -48,7 +48,9 @@ describe('Create a team and add that team as a owner of the entity', () => {
it('Add a group team type and assign it as a owner of the entity', () => { it('Add a group team type and assign it as a owner of the entity', () => {
interceptURL('GET', '/api/v1/teams/name/*', 'getTeams'); interceptURL('GET', '/api/v1/teams/name/*', 'getTeams');
cy.get('[data-testid="appbar-item-settings"]').should('be.visible').click(); cy.get('[data-testid="app-bar-item-settings"]')
.should('be.visible')
.click();
// Clicking on teams // Clicking on teams
cy.get('[data-testid="settings-left-panel"]') cy.get('[data-testid="settings-left-panel"]')

View File

@ -33,7 +33,7 @@ describe(`Advanced search quick filters should work properly for assets`, () =>
it(`should show the quick filters for respective assets`, () => { it(`should show the quick filters for respective assets`, () => {
// Navigate to explore page // Navigate to explore page
cy.get('[data-testid="appbar-item-explore"]').click(); cy.get('[data-testid="app-bar-item-explore"]').click();
QUICK_FILTERS_BY_ASSETS.map((asset) => { QUICK_FILTERS_BY_ASSETS.map((asset) => {
cy.get(`[data-testid="${asset.tab}"]`).scrollIntoView().click(); cy.get(`[data-testid="${asset.tab}"]`).scrollIntoView().click();
@ -50,7 +50,7 @@ describe(`Advanced search quick filters should work properly for assets`, () =>
const asset = QUICK_FILTERS_BY_ASSETS[0]; const asset = QUICK_FILTERS_BY_ASSETS[0];
// Navigate to explore page // Navigate to explore page
cy.get('[data-testid="appbar-item-explore"]').click(); cy.get('[data-testid="app-bar-item-explore"]').click();
cy.get(`[data-testid="${asset.tab}"]`).scrollIntoView().click(); cy.get(`[data-testid="${asset.tab}"]`).scrollIntoView().click();
asset.filters asset.filters

View File

@ -17,29 +17,29 @@ describe('Collect end point should work properly', () => {
const PAGES = { const PAGES = {
setting: { setting: {
name: 'Settings', name: 'Settings',
mainMenuId: `[data-testid="appbar-item-settings"]`, mainMenuId: `[data-testid="app-bar-item-settings"]`,
}, },
explore: { explore: {
name: 'Explore', name: 'Explore',
mainMenuId: `[data-testid="appbar-item-explore"]`, mainMenuId: `[data-testid="app-bar-item-explore"]`,
}, },
dataQuality: { dataQuality: {
name: 'Quality', name: 'Quality',
mainMenuId: `[data-testid="appbar-item-data-quality"]`, mainMenuId: `[data-testid="app-bar-item-data-quality"]`,
}, },
insight: { insight: {
name: 'Insights', name: 'Insights',
mainMenuId: `[data-testid="appbar-item-data-insight"]`, mainMenuId: `[data-testid="app-bar-item-data-insight"]`,
}, },
glossary: { glossary: {
name: 'Glossary', name: 'Glossary',
mainMenuId: `[data-testid="governance"]`, mainMenuId: `[data-testid="governance"]`,
subMenu: `[data-testid="appbar-item-glossary"]`, subMenu: `[data-testid="app-bar-item-glossary"]`,
}, },
tag: { tag: {
name: 'Tags', name: 'Tags',
mainMenuId: `[data-testid="governance"]`, mainMenuId: `[data-testid="governance"]`,
subMenu: `[data-testid="appbar-item-tags"]`, subMenu: `[data-testid="app-bar-item-tags"]`,
}, },
}; };
@ -66,7 +66,7 @@ describe('Collect end point should work properly', () => {
if (page.subMenu) { if (page.subMenu) {
// adding manual wait to open dropdown in UI // adding manual wait to open dropdown in UI
cy.wait(500); cy.wait(500);
cy.get(page.subMenu).should('be.visible').click(); cy.get(page.subMenu).should('be.visible').click({ force: true });
} }
assertCollectEndPoint(); assertCollectEndPoint();
}); });

View File

@ -21,7 +21,7 @@ describe('Logout User', () => {
it('After login logout the user and invalidate the token', () => { it('After login logout the user and invalidate the token', () => {
interceptURL('POST', '/api/v1/users/logout', 'logoutUser'); interceptURL('POST', '/api/v1/users/logout', 'logoutUser');
cy.get('[data-testid="appbar-item-logout"]').click(); cy.get('[data-testid="app-bar-item-logout"]').click();
cy.get('[data-testid="confirm-logout"]').click(); cy.get('[data-testid="confirm-logout"]').click();

View File

@ -55,7 +55,9 @@ describe.skip('pre-requests for test case', () => {
'api/v1/teams/name/Organization?fields=*', 'api/v1/teams/name/Organization?fields=*',
'getSettingsPage' 'getSettingsPage'
); );
cy.get('[data-testid="appbar-item-settings"]').should('be.visible').click(); cy.get('[data-testid="app-bar-item-settings"]')
.should('be.visible')
.click();
verifyResponseStatusCode('@getSettingsPage', 200); verifyResponseStatusCode('@getSettingsPage', 200);
// Services page // Services page
interceptURL('GET', '/api/v1/services/*', 'getServiceList'); interceptURL('GET', '/api/v1/services/*', 'getServiceList');

View File

@ -61,7 +61,7 @@ describe.skip('Alerts page should work properly', () => {
interceptURL('POST', '/api/v1/events/subscriptions', 'createAlert'); interceptURL('POST', '/api/v1/events/subscriptions', 'createAlert');
interceptURL('GET', `/api/v1/search/query?q=*`, 'getSearchResult'); interceptURL('GET', `/api/v1/search/query?q=*`, 'getSearchResult');
cy.login(); cy.login();
cy.get('[data-testid="appbar-item-settings"]') cy.get('[data-testid="app-bar-item-settings"]')
.should('exist') .should('exist')
.and('be.visible') .and('be.visible')
.click(); .click();

View File

@ -76,7 +76,7 @@ const revokeToken = () => {
describe('Bots Page should work properly', () => { describe('Bots Page should work properly', () => {
beforeEach(() => { beforeEach(() => {
cy.login(); cy.login();
cy.get('[data-testid="appbar-item-settings"]') cy.get('[data-testid="app-bar-item-settings"]')
.should('exist') .should('exist')
.should('be.visible') .should('be.visible')
.click(); .click();

View File

@ -26,7 +26,7 @@ describe('Custom Logo Config', () => {
beforeEach(() => { beforeEach(() => {
cy.login(); cy.login();
cy.get('[data-testid="appbar-item-settings"]').click(); cy.get('[data-testid="app-bar-item-settings"]').click();
interceptURL( interceptURL(
'GET', 'GET',

View File

@ -25,7 +25,9 @@ describe('Custom Properties should work properly', () => {
cy.login(); cy.login();
interceptURL('GET', '/api/v1/teams/name/*', 'settingsPage'); interceptURL('GET', '/api/v1/teams/name/*', 'settingsPage');
cy.get('[data-testid="appbar-item-settings"]').should('be.visible').click(); cy.get('[data-testid="app-bar-item-settings"]')
.should('be.visible')
.click();
verifyResponseStatusCode('@settingsPage', 200); verifyResponseStatusCode('@settingsPage', 200);
cy.get('[data-testid="settings-left-panel"]').should('be.visible'); cy.get('[data-testid="settings-left-panel"]').should('be.visible');
}); });
@ -59,7 +61,7 @@ describe('Custom Properties should work properly', () => {
); );
// Navigating back to custom properties page // Navigating back to custom properties page
cy.get('[data-testid="appbar-item-settings"]').click(); cy.get('[data-testid="app-bar-item-settings"]').click();
cy.get(`[data-menu-id*="customAttributes.${entity.name}"]`) cy.get(`[data-menu-id*="customAttributes.${entity.name}"]`)
.scrollIntoView() .scrollIntoView()
.click(); .click();
@ -130,7 +132,7 @@ describe('Custom Properties should work properly', () => {
); );
// Navigating back to custom properties page // Navigating back to custom properties page
cy.get('[data-testid="appbar-item-settings"]') cy.get('[data-testid="app-bar-item-settings"]')
.should('be.visible') .should('be.visible')
.click(); .click();
// Selecting the entity // Selecting the entity
@ -206,7 +208,7 @@ describe('Custom Properties should work properly', () => {
); );
// Navigating back to custom properties page // Navigating back to custom properties page
cy.get('[data-testid="appbar-item-settings"]') cy.get('[data-testid="app-bar-item-settings"]')
.should('be.visible') .should('be.visible')
.click(); .click();
cy.get(`[data-menu-id*="customAttributes.${entity.name}"]`) cy.get(`[data-menu-id*="customAttributes.${entity.name}"]`)

View File

@ -30,7 +30,7 @@ describe('Data Insight Alert', () => {
beforeEach(() => { beforeEach(() => {
cy.login(); cy.login();
cy.get('[data-testid="appbar-item-settings"]') cy.get('[data-testid="app-bar-item-settings"]')
.should('exist') .should('exist')
.and('be.visible') .and('be.visible')
.click(); .click();

View File

@ -21,7 +21,7 @@ describe('Data Insight settings page should work properly', () => {
cy.login(); cy.login();
interceptURL('GET', '/api/v1/teams/name/*', 'settingsPage'); interceptURL('GET', '/api/v1/teams/name/*', 'settingsPage');
cy.get('[data-testid="appbar-item-settings"]').click(); cy.get('[data-testid="app-bar-item-settings"]').click();
verifyResponseStatusCode('@settingsPage', 200); verifyResponseStatusCode('@settingsPage', 200);
cy.get('[data-testid="settings-left-panel"]').should('be.visible'); cy.get('[data-testid="settings-left-panel"]').should('be.visible');

View File

@ -98,7 +98,9 @@ describe('Data Quality and Profiler should work properly', () => {
cy.clickOnLogo(); cy.clickOnLogo();
cy.get('[data-testid="appbar-item-settings"]').should('be.visible').click(); cy.get('[data-testid="app-bar-item-settings"]')
.should('be.visible')
.click();
cy.get('[data-menu-id*="databases"]').should('be.visible').click(); cy.get('[data-menu-id*="databases"]').should('be.visible').click();
cy.intercept('/api/v1/services/ingestionPipelines?*').as('ingestionData'); cy.intercept('/api/v1/services/ingestionPipelines?*').as('ingestionData');
interceptURL( interceptURL(
@ -443,7 +445,7 @@ describe('Data Quality and Profiler should work properly', () => {
'/api/v1/search/query?q=*&index=test_case_search_index*', '/api/v1/search/query?q=*&index=test_case_search_index*',
'getTestCase' 'getTestCase'
); );
cy.get('[data-testid="appbar-item-data-quality"]').click(); cy.get('[data-testid="app-bar-item-data-quality"]').click();
cy.get('[data-testid="by-test-suites"]').click(); cy.get('[data-testid="by-test-suites"]').click();
verifyResponseStatusCode('@testSuite', 200); verifyResponseStatusCode('@testSuite', 200);
cy.get('[data-testid="add-test-suite-btn"]').click(); cy.get('[data-testid="add-test-suite-btn"]').click();
@ -483,7 +485,7 @@ describe('Data Quality and Profiler should work properly', () => {
'/api/v1/dataQuality/testCases/logicalTestCases', '/api/v1/dataQuality/testCases/logicalTestCases',
'putTestCase' 'putTestCase'
); );
cy.get('[data-testid="appbar-item-data-quality"]').click(); cy.get('[data-testid="app-bar-item-data-quality"]').click();
cy.get('[data-testid="by-test-suites"]').click(); cy.get('[data-testid="by-test-suites"]').click();
verifyResponseStatusCode('@testSuite', 200); verifyResponseStatusCode('@testSuite', 200);
cy.get('[data-testid="test-suite-container"]') cy.get('[data-testid="test-suite-container"]')
@ -520,7 +522,7 @@ describe('Data Quality and Profiler should work properly', () => {
'/api/v1/dataQuality/testCases/logicalTestCases/*/*', '/api/v1/dataQuality/testCases/logicalTestCases/*/*',
'removeTestCase' 'removeTestCase'
); );
cy.get('[data-testid="appbar-item-data-quality"]').click(); cy.get('[data-testid="app-bar-item-data-quality"]').click();
cy.get('[data-testid="by-test-suites"]').click(); cy.get('[data-testid="by-test-suites"]').click();
verifyResponseStatusCode('@testSuite', 200); verifyResponseStatusCode('@testSuite', 200);
cy.get('[data-testid="test-suite-container"]') cy.get('[data-testid="test-suite-container"]')
@ -544,7 +546,7 @@ describe('Data Quality and Profiler should work properly', () => {
'/api/v1/dataQuality/testSuites?fields=*&testSuiteType=logical', '/api/v1/dataQuality/testSuites?fields=*&testSuiteType=logical',
'testSuite' 'testSuite'
); );
cy.get('[data-testid="appbar-item-data-quality"]').click(); cy.get('[data-testid="app-bar-item-data-quality"]').click();
cy.get('[data-testid="by-test-suites"]').click(); cy.get('[data-testid="by-test-suites"]').click();
verifyResponseStatusCode('@testSuite', 200); verifyResponseStatusCode('@testSuite', 200);
cy.get('[data-testid="test-suite-container"]') cy.get('[data-testid="test-suite-container"]')

View File

@ -394,10 +394,10 @@ describe('Glossary page should work properly', () => {
.and('be.visible') .and('be.visible')
.then(($el) => { .then(($el) => {
cy.wrap($el) cy.wrap($el)
.find('[data-testid="appbar-item-glossary"]') .find('[data-testid="app-bar-item-glossary"]')
.should('exist') .should('exist')
.and('be.visible') .and('be.visible')
.click(); .click({ force: true });
}); });
}); });
@ -786,7 +786,7 @@ describe('Glossary page should work properly', () => {
.should('contain', term3); .should('contain', term3);
cy.get('[data-testid="governance"]').click(); cy.get('[data-testid="governance"]').click();
cy.get('[data-testid="appbar-item-glossary"]').click(); cy.get('[data-testid="app-bar-item-glossary"]').click({ force: true });
cy.get('.ant-menu-item').contains(NEW_GLOSSARY_1.name).click(); cy.get('.ant-menu-item').contains(NEW_GLOSSARY_1.name).click();
@ -859,7 +859,7 @@ describe('Glossary page should work properly', () => {
.and('not.contain', 'Personal'); .and('not.contain', 'Personal');
cy.get('[data-testid="governance"]').click(); cy.get('[data-testid="governance"]').click();
cy.get('[data-testid="appbar-item-glossary"]').click(); cy.get('[data-testid="app-bar-item-glossary"]').click({ force: true });
selectActiveGlossary(NEW_GLOSSARY_1.name); selectActiveGlossary(NEW_GLOSSARY_1.name);

View File

@ -100,7 +100,9 @@ describe('Policy page should work properly', () => {
cy.login(); cy.login();
cy.intercept('GET', '*api/v1/policies*').as('getPolicies'); cy.intercept('GET', '*api/v1/policies*').as('getPolicies');
cy.get('[data-testid="appbar-item-settings"]').should('be.visible').click(); cy.get('[data-testid="app-bar-item-settings"]')
.should('be.visible')
.click();
cy.get('[data-testid="settings-left-panel"]') cy.get('[data-testid="settings-left-panel"]')
.contains('Policies') .contains('Policies')

View File

@ -61,7 +61,9 @@ describe('Roles page should work properly', () => {
interceptURL('GET', '*api/v1/roles*', 'getRoles'); interceptURL('GET', '*api/v1/roles*', 'getRoles');
cy.get('[data-testid="appbar-item-settings"]').should('be.visible').click(); cy.get('[data-testid="app-bar-item-settings"]')
.should('be.visible')
.click();
cy.get('[data-testid="settings-left-panel"]') cy.get('[data-testid="settings-left-panel"]')
.contains('Roles') .contains('Roles')

View File

@ -38,7 +38,9 @@ describe('Services page should work properly', () => {
cy.login(); cy.login();
// redirecting to services page // redirecting to services page
cy.get('[data-testid="appbar-item-settings"]').should('be.visible').click(); cy.get('[data-testid="app-bar-item-settings"]')
.should('be.visible')
.click();
cy.get('[data-testid="settings-left-panel"]') cy.get('[data-testid="settings-left-panel"]')
.contains('Database') .contains('Database')

View File

@ -121,7 +121,7 @@ describe('Tags page should work', () => {
// adding manual wait to open dropdown in UI // adding manual wait to open dropdown in UI
cy.wait(500); cy.wait(500);
cy.get('[data-testid="appbar-item-tags"]').should('be.visible').click(); cy.get('[data-testid="app-bar-item-tags"]').should('be.visible').click();
verifyResponseStatusCode('@getTags', 200); verifyResponseStatusCode('@getTags', 200);
}); });

View File

@ -53,7 +53,9 @@ describe('Teams flow should work properly', () => {
interceptURL('GET', `/api/v1/permissions/team/name/*`, 'permissions'); interceptURL('GET', `/api/v1/permissions/team/name/*`, 'permissions');
cy.login(); cy.login();
cy.get('[data-testid="appbar-item-settings"]').should('be.visible').click(); cy.get('[data-testid="app-bar-item-settings"]')
.should('be.visible')
.click();
// Clicking on teams // Clicking on teams
cy.get('[data-testid="settings-left-panel"]') cy.get('[data-testid="settings-left-panel"]')

View File

@ -34,7 +34,7 @@ describe('Users flow should work properly', () => {
beforeEach(() => { beforeEach(() => {
cy.login(); cy.login();
cy.get('[data-testid="appbar-item-settings"]') cy.get('[data-testid="app-bar-item-settings"]')
.should('exist') .should('exist')
.should('be.visible') .should('be.visible')
.click(); .click();
@ -82,7 +82,7 @@ describe('Admin flow should work properly', () => {
beforeEach(() => { beforeEach(() => {
cy.login(); cy.login();
cy.get('[data-testid="appbar-item-settings"]') cy.get('[data-testid="app-bar-item-settings"]')
.should('exist') .should('exist')
.should('be.visible') .should('be.visible')
.click(); .click();

View File

@ -10,22 +10,21 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
import { Button, Col, Menu, MenuProps, Row, Typography } from 'antd'; import { Button, Col, Menu, MenuProps, Row, Tooltip, Typography } from 'antd';
import Modal from 'antd/lib/modal/Modal'; import Modal from 'antd/lib/modal/Modal';
import { ReactComponent as GovernIcon } from 'assets/svg/bank.svg'; import { ReactComponent as GovernIcon } from 'assets/svg/bank.svg';
import { ReactComponent as ClassificationIcon } from 'assets/svg/classification.svg';
import { ReactComponent as ExploreIcon } from 'assets/svg/globalsearch.svg';
import { ReactComponent as GlossaryIcon } from 'assets/svg/glossary.svg';
import { ReactComponent as QualityIcon } from 'assets/svg/ic-quality-v1.svg';
import { ReactComponent as SettingsIcon } from 'assets/svg/ic-settings-v1.svg';
import { ReactComponent as InsightsIcon } from 'assets/svg/lampcharge.svg';
import { ReactComponent as LogoutIcon } from 'assets/svg/logout.svg'; import { ReactComponent as LogoutIcon } from 'assets/svg/logout.svg';
import { useAuthContext } from 'components/authentication/auth-provider/AuthProvider'; import { useAuthContext } from 'components/authentication/auth-provider/AuthProvider';
import { ROUTES } from 'constants/constants'; import {
SETTING_ITEM,
SIDEBAR_GOVERN_LIST,
SIDEBAR_LIST,
} from 'constants/LeftSidebar.constants';
import React, { useMemo, useState } from 'react'; import React, { useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { NavLink } from 'react-router-dom'; import { NavLink } from 'react-router-dom';
import './left-sidebar.less'; import './left-sidebar.less';
import LeftSidebarItem from './LeftSidebarItem.component';
const LeftSidebar = () => { const LeftSidebar = () => {
const { t } = useTranslation(); const { t } = useTranslation();
@ -48,53 +47,45 @@ const LeftSidebar = () => {
key: 'governance', key: 'governance',
popupClassName: 'govern-menu', popupClassName: 'govern-menu',
label: ( label: (
<div <Tooltip
className="d-flex flex-col items-center" overlayClassName="left-panel-tooltip"
data-testid="governance"> placement="right"
<GovernIcon className="m-0" width={30} /> title={
<Typography.Text className="left-panel-label"> <Typography.Text className="left-panel-label">
{t('label.govern', { lng: 'en-US' })} {t('label.govern')}
</Typography.Text> </Typography.Text>
</div> }>
<GovernIcon data-testid="governance" width={30} />
</Tooltip>
),
children: SIDEBAR_GOVERN_LIST.map(
({ key, label, icon, redirect_url, dataTestId }) => {
const Icon = icon;
return {
key,
label: (
<Tooltip
overlayClassName="left-panel-tooltip"
placement="right"
title={
<Typography.Text className="left-panel-label">
{label}
</Typography.Text>
}>
<NavLink
className="no-underline d-flex justify-center"
data-testid={dataTestId}
to={{
pathname: redirect_url,
}}>
<Icon className="left-panel-item p-y-sm" width={30} />
</NavLink>
</Tooltip>
),
};
}
), ),
children: [
{
key: 'glossary',
label: (
<NavLink
className="no-underline"
data-testid="appbar-item-glossary"
to={{
pathname: ROUTES.GLOSSARY,
}}>
<span className="left-panel-item p-y-xss">
<GlossaryIcon className="m-0" width={30} />
<Typography.Text className="left-panel-label">
{t('label.glossary', { lng: 'en-US' })}
</Typography.Text>
</span>
</NavLink>
),
},
{
key: 'tags',
label: (
<NavLink
className="no-underline"
data-testid="appbar-item-tags"
to={{
pathname: ROUTES.TAGS,
}}>
<div className="left-panel-item p-y-xss">
<ClassificationIcon className="m-0" width={30} />
<Typography.Text className="left-panel-label">
{t('label.classification', { lng: 'en-US' })}
</Typography.Text>
</div>
</NavLink>
),
},
],
}, },
]; ];
}, []); }, []);
@ -103,67 +94,18 @@ const LeftSidebar = () => {
setShowConfirmLogoutModal(true); setShowConfirmLogoutModal(true);
}; };
const hideCofirmationModal = () => { const hideConfirmationModal = () => {
setShowConfirmLogoutModal(false); setShowConfirmLogoutModal(false);
}; };
return ( return (
<div className="d-flex flex-col justify-between h-full"> <div className="d-flex flex-col justify-between h-full">
<Row className="p-y-sm"> <Row className="p-y-sm">
<Col span={24}> {SIDEBAR_LIST.map((item) => (
<NavLink <Col key={item.key} span={24}>
className="no-underline" <LeftSidebarItem data={item} />
data-testid="appbar-item-explore" </Col>
to={{ ))}
pathname: '/explore/tables',
}}>
<div
className={`left-panel-item ${
location.pathname.startsWith('/explore') ? 'active' : ''
}`}>
<ExploreIcon className="m-0" width={30} />
<Typography.Text className="left-panel-label">
{t('label.explore', { lng: 'en-US' })}
</Typography.Text>
</div>
</NavLink>
</Col>
<Col span={24}>
<NavLink
className="no-underline"
data-testid="appbar-item-data-quality"
to={{
pathname: ROUTES.DATA_QUALITY,
}}>
<div
className={`left-panel-item ${
location.pathname.includes(ROUTES.DATA_QUALITY) ? 'active' : ''
}`}>
<QualityIcon className="m-0" width={30} />
<Typography.Text className="left-panel-label">
{t('label.quality', { lng: 'en-US' })}
</Typography.Text>
</div>
</NavLink>
</Col>
<Col span={24}>
<NavLink
className="no-underline"
data-testid="appbar-item-data-insight"
to={{
pathname: ROUTES.DATA_INSIGHT,
}}>
<div
className={`left-panel-item ${
location.pathname.includes(ROUTES.DATA_INSIGHT) ? 'active' : ''
}`}>
<InsightsIcon className="m-0" width={30} />
<Typography.Text className="left-panel-label">
{t('label.insight-plural', { lng: 'en-US' })}
</Typography.Text>
</div>
</NavLink>
</Col>
<Menu <Menu
className="left-panel-item" className="left-panel-item"
items={items} items={items}
@ -174,33 +116,24 @@ const LeftSidebar = () => {
</Row> </Row>
<Row className="p-y-sm"> <Row className="p-y-sm">
<Col span={24}> <Col span={24}>
<NavLink <LeftSidebarItem data={SETTING_ITEM} />
className="no-underline"
data-testid="appbar-item-settings"
to={{
pathname: ROUTES.SETTINGS,
}}>
<div
className={`left-panel-item ${
location.pathname.startsWith('/settings') ? 'active' : ''
}`}>
<SettingsIcon className="m-0" width={30} />
<Typography.Text className="left-panel-label">
{t('label.setting-plural', { lng: 'en-US' })}
</Typography.Text>
</div>
</NavLink>
</Col> </Col>
<Col span={24}> <Col span={24}>
<div <Tooltip
className="left-panel-item cursor-pointer" overlayClassName="left-panel-tooltip"
data-testid="appbar-item-logout" placement="right"
onClick={handleLogoutClick}> title={
<LogoutIcon className="m-0" width={30} /> <Typography.Text className="left-panel-label">
<Typography.Text className="left-panel-label"> {t('label.logout')}
{t('label.logout', { lng: 'en-US' })} </Typography.Text>
</Typography.Text> }>
</div> <div
className="left-panel-item"
data-testid="app-bar-item-logout"
onClick={handleLogoutClick}>
<LogoutIcon className="m-0" width={30} />
</div>
</Tooltip>
</Col> </Col>
</Row> </Row>
{showConfirmLogoutModal && ( {showConfirmLogoutModal && (
@ -212,14 +145,14 @@ const LeftSidebar = () => {
footer={null} footer={null}
open={showConfirmLogoutModal} open={showConfirmLogoutModal}
width={360} width={360}
onCancel={hideCofirmationModal}> onCancel={hideConfirmationModal}>
<Typography.Title level={5}>{t('label.logout')}</Typography.Title> <Typography.Title level={5}>{t('label.logout')}</Typography.Title>
<Typography.Text className="text-grey-muted"> <Typography.Text className="text-grey-muted">
{t('message.logout-confirmation')} {t('message.logout-confirmation')}
</Typography.Text> </Typography.Text>
<div className="d-flex gap-2 w-full m-t-md justify-center"> <div className="d-flex gap-2 w-full m-t-md justify-center">
<Button className="confirm-btn" onClick={hideCofirmationModal}> <Button className="confirm-btn" onClick={hideConfirmationModal}>
{t('label.cancel')} {t('label.cancel')}
</Button> </Button>
<Button <Button

View File

@ -23,10 +23,10 @@ describe('LeftSidebar', () => {
</BrowserRouter> </BrowserRouter>
); );
const exploreLink = screen.getByTestId('appbar-item-explore'); const exploreLink = screen.getByTestId('app-bar-item-explore');
const qualityLink = screen.getByTestId('appbar-item-data-quality'); const qualityLink = screen.getByTestId('app-bar-item-data-quality');
const insightLink = screen.getByTestId('appbar-item-data-insight'); const insightLink = screen.getByTestId('app-bar-item-data-insight');
const settingsLink = screen.getByTestId('appbar-item-settings'); const settingsLink = screen.getByTestId('app-bar-item-settings');
expect(exploreLink).toBeInTheDocument(); expect(exploreLink).toBeInTheDocument();
expect(qualityLink).toBeInTheDocument(); expect(qualityLink).toBeInTheDocument();

View File

@ -0,0 +1,57 @@
/*
* Copyright 2023 Collate.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Tooltip, Typography } from 'antd';
import classNames from 'classnames';
import React from 'react';
import { NavLink } from 'react-router-dom';
interface LeftSidebarItemProps {
data: {
key: string;
label: string;
dataTestId: string;
redirect_url: string;
icon: SvgComponent;
};
}
const LeftSidebarItem = ({
data: { key, label, icon, redirect_url, dataTestId },
}: LeftSidebarItemProps) => {
const Icon = icon;
return (
<Tooltip
overlayClassName="left-panel-tooltip"
placement="right"
title={
<Typography.Text className="left-panel-label">{label}</Typography.Text>
}>
<NavLink
className={classNames(
'no-underline d-flex justify-center left-panel-item',
{
active: location.pathname.startsWith(key),
}
)}
data-testid={dataTestId}
to={{
pathname: redirect_url,
}}>
<Icon width={30} />
</NavLink>
</Tooltip>
);
};
export default LeftSidebarItem;

View File

@ -0,0 +1,31 @@
/*
* Copyright 2023 Collate.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { render, screen } from '@testing-library/react';
import { SETTING_ITEM } from 'constants/LeftSidebar.constants';
import React from 'react';
import { BrowserRouter } from 'react-router-dom';
import LeftSidebarItem from './LeftSidebarItem.component';
describe('LeftSidebar Items', () => {
it('renders sidebar items data', () => {
render(
<BrowserRouter>
<LeftSidebarItem data={SETTING_ITEM} />
</BrowserRouter>
);
const item = screen.getByTestId('app-bar-item-settings');
expect(item).toBeInTheDocument();
});
});

View File

@ -54,6 +54,13 @@
} }
.left-panel-item { .left-panel-item {
cursor: pointer;
color: @text-grey-muted;
.ant-menu-submenu-vertical {
.ant-menu-submenu-title {
margin: 0;
}
}
svg { svg {
color: @text-grey-muted; color: @text-grey-muted;
} }
@ -71,16 +78,12 @@
svg { svg {
color: @primary-color; color: @primary-color;
} }
.left-panel-label {
color: @text-color;
}
} }
.govern-menu { .govern-menu {
width: 100px !important; width: 100px !important;
.ant-menu-item { .ant-menu-item {
height: auto !important; height: auto !important;
padding: 8px 0 0 !important;
margin: 0; margin: 0;
} }
.ant-menu-item:hover, .ant-menu-item:hover,
@ -95,18 +98,25 @@
} }
} }
.ant-menu-vertical { .ant-menu-vertical {
min-width: 100px !important; min-width: 58px !important;
width: 58px;
margin-top: 3px;
margin-left: 4px;
.ant-menu-item { .ant-menu-item {
margin: 0; margin: 0;
} }
.ant-menu-item:not(:last-child) {
margin: 0;
}
} }
} }
.left-panel-label.ant-typography { .left-panel-label.ant-typography {
font-size: 12px; font-size: 12px;
color: @text-grey-muted; color: @white;
font-weight: 500; font-weight: 500;
padding-top: 0.5rem; padding: 0 20px;
line-height: 26px; line-height: 26px;
text-decoration: none; text-decoration: none;
} }
@ -114,3 +124,10 @@
.confirm-btn { .confirm-btn {
width: 130px; width: 130px;
} }
.left-panel-tooltip {
.ant-tooltip-inner {
padding: 0;
min-height: auto;
}
}

View File

@ -0,0 +1,70 @@
/*
* Copyright 2023 Collate.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ReactComponent as ClassificationIcon } from 'assets/svg/classification.svg';
import { ReactComponent as ExploreIcon } from 'assets/svg/globalsearch.svg';
import { ReactComponent as GlossaryIcon } from 'assets/svg/glossary.svg';
import { ReactComponent as QualityIcon } from 'assets/svg/ic-quality-v1.svg';
import { ReactComponent as SettingsIcon } from 'assets/svg/ic-settings-v1.svg';
import { ReactComponent as InsightsIcon } from 'assets/svg/lampcharge.svg';
import i18next from 'i18next';
import { ROUTES } from './constants';
export const SIDEBAR_LIST = [
{
key: ROUTES.EXPLORE,
label: i18next.t('label.explore'),
redirect_url: '/explore/tables',
icon: ExploreIcon,
dataTestId: 'app-bar-item-explore',
},
{
key: ROUTES.DATA_QUALITY,
label: i18next.t('label.quality'),
redirect_url: ROUTES.DATA_QUALITY,
icon: QualityIcon,
dataTestId: 'app-bar-item-data-quality',
},
{
key: ROUTES.DATA_INSIGHT,
label: i18next.t('label.insight-plural'),
redirect_url: ROUTES.DATA_INSIGHT,
icon: InsightsIcon,
dataTestId: 'app-bar-item-data-insight',
},
];
export const SIDEBAR_GOVERN_LIST = [
{
key: 'glossary',
label: i18next.t('label.glossary'),
redirect_url: ROUTES.GLOSSARY,
icon: GlossaryIcon,
dataTestId: 'app-bar-item-glossary',
},
{
key: 'tags',
label: i18next.t('label.classification'),
redirect_url: ROUTES.TAGS,
icon: ClassificationIcon,
dataTestId: 'app-bar-item-tags',
},
];
export const SETTING_ITEM = {
key: ROUTES.SETTINGS,
label: i18next.t('label.setting-plural'),
redirect_url: ROUTES.SETTINGS,
icon: SettingsIcon,
dataTestId: 'app-bar-item-settings',
};