From fd32b57f7d59b7cefb1160e224bd6bec972b0417 Mon Sep 17 00:00:00 2001 From: Shaheer <122449658+shaheer-deriv@users.noreply.github.com> Date: Fri, 3 Nov 2023 10:17:25 +0400 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=94=A9=20updated=20'Connected=20a?= =?UTF-8?q?pps'=20page=20in=20profile=20settings=20(#10314)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: :truck: migrates js test file to tsx * feat: :sparkles: adds connected apps earn more section * refactor: :art: updates connected apps know more section * refactor: :art: removes unused icon * refactor: :truck: migrates index js to ts * feat: :sparkles: adds connected apps empty component * refactor: :art: refactors article section * style: :lipstick: refactors classname * refactor: :truck: migrates test file from jsx to tsx * refactor: :art: refactors empty connected apps component * style: :lipstick: cleans account styling for connected apps component * refactor: :art: refactors connected apps section * fix: :bug: fixes typescript warnings * refactor: :art: refactors connected apps screen * test: :test_tube: adds test for connected apps component * test: :test_tube: adds missing test to article component * test: :test_tube: adds test for earn more section * refactor: :art: refactors connected apps component * test: :test_tube: adds test for know more section * test: :test_tube: adds test for connected apps empty component * test: :test_tube: adds test for data list template entry * test: :test_tube: adds test for data list template * refactor: :art: refactors connected apps component * refactor: :art: refactors tsx file * test: :test_tube: adds test for useOauthConnectedApps hook * test: :test_tube: adds test for useOAuthRevokeConnectedApps hook * test: :test_tube: updates connected apps test file * feat: dumy * feat: added case with atleast one connected app * chore: review changes * style: :lipstick: updates style * refactor: :art: removes unwanted observer * refactor: :art: uses react fragment instead of div * feat: empty commit * feat: empty commit * fix: make the desktop design similar to figma * fix: :bug: fixes code review issues * fix: :bug: fixes as per code review comments * revert: :ambulance: reverts hook usage due to mobx api clash * revert: :ambulance: reverts hook implementation due to mobx api sync issue * refactor: :wastebasket: clean up index file * test: :test_tube: updates test cases * style: :lipstick: updates info component text size based on responsiveness * refactor: :wastebasket: removes unused import * test: :test_tube: fixes test case * refactor: :art: refactors code based on code review * refactor: :lipstick: moves styling to local style file * refactor: :art: passes prop directly * refactor: :art: creates a common bullet ol component * test: :test_tube: updates test cases * refactor: :art: refactors bullets ol list * test: :test_tube: updates test file * fix: :bug: sets loading screen based on error in BE response * refactor: :art: passes classnames directly * refactor: :art: updates article component to take Localize --------- Co-authored-by: sanjam chhatwal Co-authored-by: sanjam-deriv <99465624+sanjam-deriv@users.noreply.github.com> --- .../article/__tests__/article.spec.tsx | 8 + .../src/Components/article/article.tsx | 2 +- .../src/Constants/connected-apps-config.tsx | 23 ++ .../connected-apps-earn-more.spec.tsx | 15 + .../__tests__/connected-apps-empty.spec.tsx | 36 +++ .../connected-apps-info-bullets.spec.tsx | 17 ++ .../__tests__/connected-apps-info.spec.tsx | 47 ++++ .../connected-apps-know-more.spec.tsx | 15 + .../__tests__/connected-apps.spec.js | 84 ------ .../__tests__/connected-apps.spec.tsx | 165 +++++++++++ .../data-list-template-entry.spec.tsx | 16 ++ .../__tests__/data-list-template.spec.tsx | 41 +++ .../connected-apps-earn-more.tsx | 22 ++ .../ConnectedApps/connected-apps-empty.tsx | 23 ++ .../connected-apps-info-bullets.tsx | 32 +++ .../ConnectedApps/connected-apps-info.tsx | 29 ++ ...ticle.tsx => connected-apps-know-more.tsx} | 13 +- .../connected-apps-revoke-modal.tsx | 38 +++ .../ConnectedApps/connected-apps.scss | 188 +++++++++++++ .../Security/ConnectedApps/connected-apps.tsx | 192 +++++-------- .../data-list-template-entry.tsx | 18 ++ .../ConnectedApps/data-list-template.tsx | 40 +++ .../ConnectedApps/data-table-template.tsx | 66 +---- .../ConnectedApps/{index.js => index.ts} | 0 .../ConnectedApps/template-helper.tsx | 34 +++ packages/account/src/Styles/account.scss | 265 ------------------ .../common/ic-account-trash-can-dashboard.svg | 1 - .../components/src/components/icon/icons.js | 1 - packages/components/stories/icon/icons.js | 1 - 29 files changed, 898 insertions(+), 534 deletions(-) create mode 100644 packages/account/src/Constants/connected-apps-config.tsx create mode 100644 packages/account/src/Sections/Security/ConnectedApps/__tests__/connected-apps-earn-more.spec.tsx create mode 100644 packages/account/src/Sections/Security/ConnectedApps/__tests__/connected-apps-empty.spec.tsx create mode 100644 packages/account/src/Sections/Security/ConnectedApps/__tests__/connected-apps-info-bullets.spec.tsx create mode 100644 packages/account/src/Sections/Security/ConnectedApps/__tests__/connected-apps-info.spec.tsx create mode 100644 packages/account/src/Sections/Security/ConnectedApps/__tests__/connected-apps-know-more.spec.tsx delete mode 100644 packages/account/src/Sections/Security/ConnectedApps/__tests__/connected-apps.spec.js create mode 100644 packages/account/src/Sections/Security/ConnectedApps/__tests__/connected-apps.spec.tsx create mode 100644 packages/account/src/Sections/Security/ConnectedApps/__tests__/data-list-template-entry.spec.tsx create mode 100644 packages/account/src/Sections/Security/ConnectedApps/__tests__/data-list-template.spec.tsx create mode 100644 packages/account/src/Sections/Security/ConnectedApps/connected-apps-earn-more.tsx create mode 100644 packages/account/src/Sections/Security/ConnectedApps/connected-apps-empty.tsx create mode 100644 packages/account/src/Sections/Security/ConnectedApps/connected-apps-info-bullets.tsx create mode 100644 packages/account/src/Sections/Security/ConnectedApps/connected-apps-info.tsx rename packages/account/src/Sections/Security/ConnectedApps/{connected-apps-article.tsx => connected-apps-know-more.tsx} (63%) create mode 100644 packages/account/src/Sections/Security/ConnectedApps/connected-apps-revoke-modal.tsx create mode 100644 packages/account/src/Sections/Security/ConnectedApps/connected-apps.scss create mode 100644 packages/account/src/Sections/Security/ConnectedApps/data-list-template-entry.tsx create mode 100644 packages/account/src/Sections/Security/ConnectedApps/data-list-template.tsx rename packages/account/src/Sections/Security/ConnectedApps/{index.js => index.ts} (100%) create mode 100644 packages/account/src/Sections/Security/ConnectedApps/template-helper.tsx delete mode 100644 packages/components/src/components/icon/common/ic-account-trash-can-dashboard.svg diff --git a/packages/account/src/Components/article/__tests__/article.spec.tsx b/packages/account/src/Components/article/__tests__/article.spec.tsx index 11fbd953aa66..7691c740f816 100644 --- a/packages/account/src/Components/article/__tests__/article.spec.tsx +++ b/packages/account/src/Components/article/__tests__/article.spec.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { screen, render } from '@testing-library/react'; import AccountArticle, { TArticle } from '../article'; +import userEvent from '@testing-library/user-event'; describe('', () => { const props: TArticle = { @@ -38,4 +39,11 @@ describe('', () => { expect(screen.getByText('Description 3')).toBeInTheDocument(); expect(screen.getByText('Description 4')).toBeInTheDocument(); }); + + it("should invoke the callback on clicking the 'Learn more' link", () => { + render(); + + userEvent.click(screen.getByText(/Learn more/i)); + expect(props.onClickLearnMore).toHaveBeenCalledTimes(1); + }); }); diff --git a/packages/account/src/Components/article/article.tsx b/packages/account/src/Components/article/article.tsx index 52a5484112be..9febac3c3ddb 100644 --- a/packages/account/src/Components/article/article.tsx +++ b/packages/account/src/Components/article/article.tsx @@ -10,7 +10,7 @@ type TDescriptionsItem = { }; export type TArticle = { - title: string; + title: JSX.Element | string; descriptions: Array; onClickLearnMore?: () => void; className?: string; diff --git a/packages/account/src/Constants/connected-apps-config.tsx b/packages/account/src/Constants/connected-apps-config.tsx new file mode 100644 index 000000000000..18fd3e3f4b6a --- /dev/null +++ b/packages/account/src/Constants/connected-apps-config.tsx @@ -0,0 +1,23 @@ +import React from 'react'; +import { Localize } from '@deriv/translations'; + +export const CONNECTED_APPS_INFO_BULLETS = [ + { + key: 1, + text: ( + + ), + }, + { + key: 2, + text: ( + + ), + }, + { + key: 3, + text: ( + + ), + }, +]; diff --git a/packages/account/src/Sections/Security/ConnectedApps/__tests__/connected-apps-earn-more.spec.tsx b/packages/account/src/Sections/Security/ConnectedApps/__tests__/connected-apps-earn-more.spec.tsx new file mode 100644 index 000000000000..b83ceddd9cd9 --- /dev/null +++ b/packages/account/src/Sections/Security/ConnectedApps/__tests__/connected-apps-earn-more.spec.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import ConnectedAppsEarnMore from '../connected-apps-earn-more'; + +describe('ConnectedAppsEarnMore', () => { + it("should render the 'Earn more' section with correct details", () => { + render(); + expect(screen.getByText(/Earn more with Deriv API/i)).toBeInTheDocument(); + expect( + screen.getByText( + /Use our powerful, flexible, and free API to build a custom trading platform for yourself or for your business./i + ) + ).toBeInTheDocument(); + }); +}); diff --git a/packages/account/src/Sections/Security/ConnectedApps/__tests__/connected-apps-empty.spec.tsx b/packages/account/src/Sections/Security/ConnectedApps/__tests__/connected-apps-empty.spec.tsx new file mode 100644 index 000000000000..0a1cf5cd80f8 --- /dev/null +++ b/packages/account/src/Sections/Security/ConnectedApps/__tests__/connected-apps-empty.spec.tsx @@ -0,0 +1,36 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { StoreProvider, mockStore } from '@deriv/stores'; +import ConnectedAppsEmpty from '../connected-apps-empty'; + +describe('ConnectedAppsEmpty', () => { + const renderComponent = (mock_store = mockStore({})) => + render( + + + + ); + + it('should render the empty apps informative text component with correct details', () => { + renderComponent(); + + expect( + screen.getByText(/You currently don't have any third-party authorised apps associated with your account./i) + ).toBeInTheDocument(); + expect( + screen.getByText( + /Connected apps are authorised applications associated with your account through your API token or the OAuth authorisation process. They can act on your behalf within the limitations that you have set./i + ) + ).toBeInTheDocument(); + expect( + screen.getByText( + /As a user, you are responsible for sharing access and for actions that occur in your account \(even if they were initiated by a third-party app on your behalf\)./i + ) + ).toBeInTheDocument(); + expect( + screen.getByText( + /Please note that only third-party apps will be displayed on this page. Official Deriv apps will not appear here./i + ) + ).toBeInTheDocument(); + }); +}); diff --git a/packages/account/src/Sections/Security/ConnectedApps/__tests__/connected-apps-info-bullets.spec.tsx b/packages/account/src/Sections/Security/ConnectedApps/__tests__/connected-apps-info-bullets.spec.tsx new file mode 100644 index 000000000000..4de4742dc459 --- /dev/null +++ b/packages/account/src/Sections/Security/ConnectedApps/__tests__/connected-apps-info-bullets.spec.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { StoreProvider, mockStore } from '@deriv/stores'; +import ConnectedAppsInfoBullets from '../connected-apps-info-bullets'; + +describe('ConnectedAppsInfoBullets', () => { + it('should render the 3 informative ordered list items', () => { + render( + + {' '} + + ); + + const ordered_list = screen.getAllByRole('listitem'); + expect(ordered_list).toHaveLength(3); + }); +}); diff --git a/packages/account/src/Sections/Security/ConnectedApps/__tests__/connected-apps-info.spec.tsx b/packages/account/src/Sections/Security/ConnectedApps/__tests__/connected-apps-info.spec.tsx new file mode 100644 index 000000000000..fa4e2a8f1855 --- /dev/null +++ b/packages/account/src/Sections/Security/ConnectedApps/__tests__/connected-apps-info.spec.tsx @@ -0,0 +1,47 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { StoreProvider, mockStore } from '@deriv/stores'; +import ConnectedAppsInfo from '../connected-apps-info'; + +describe('', () => { + beforeEach(() => { + render( + + + + ); + }); + + it('should have h4 element with text "What are connected apps"', () => { + const heading = screen.getByRole('heading', { name: 'What are connected apps?' }); + expect(heading).toBeInTheDocument(); + }); + + it('should have an ordered list', () => { + const orderedlist = screen.getByRole('list'); + expect(orderedlist).toBeInTheDocument(); + }); + + it('should have three list items', () => { + const listitems = screen.getAllByRole('listitem'); + expect(listitems).toHaveLength(3); + }); + + it('displays connected apps information', () => { + expect( + screen.getByText( + 'Connected apps are authorised applications associated with your account through your API token or the OAuth authorisation process. They can act on your behalf within the limitations that you have set.' + ) + ).toBeInTheDocument(); + expect( + screen.getByText( + 'As a user, you are responsible for sharing access and for actions that occur in your account (even if they were initiated by a third-party app on your behalf).' + ) + ).toBeInTheDocument(); + expect( + screen.getByText( + 'Please note that only third-party apps will be displayed on this page. Official Deriv apps will not appear here.' + ) + ).toBeInTheDocument(); + }); +}); diff --git a/packages/account/src/Sections/Security/ConnectedApps/__tests__/connected-apps-know-more.spec.tsx b/packages/account/src/Sections/Security/ConnectedApps/__tests__/connected-apps-know-more.spec.tsx new file mode 100644 index 000000000000..24d945890d2d --- /dev/null +++ b/packages/account/src/Sections/Security/ConnectedApps/__tests__/connected-apps-know-more.spec.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import ConnectedAppsKnowMore from '../connected-apps-know-more'; + +describe('ConnectedAppsKnowMore', () => { + it("should render the 'Know more' section with correct details", () => { + render(); + expect(screen.getByText(/Want to know more about APIs\?/i)).toBeInTheDocument(); + expect( + screen.getByText( + /Go to our Deriv community and learn about APIs, API tokens, ways to use Deriv APIs, and more./i + ) + ).toBeInTheDocument(); + }); +}); diff --git a/packages/account/src/Sections/Security/ConnectedApps/__tests__/connected-apps.spec.js b/packages/account/src/Sections/Security/ConnectedApps/__tests__/connected-apps.spec.js deleted file mode 100644 index 4d8f443310c8..000000000000 --- a/packages/account/src/Sections/Security/ConnectedApps/__tests__/connected-apps.spec.js +++ /dev/null @@ -1,84 +0,0 @@ -import React from 'react'; -import { render, screen, waitFor, waitForElementToBeRemoved, fireEvent, act } from '@testing-library/react'; -import ConnectedApps from '../connected-apps'; - -const true_oauth_apps_list = { - oauth_apps: [ - { - name: 'Local', - app_markup_percentage: 0, - app_id: 9999, - scopes: ['read', 'admin', 'trade', 'payments'], - last_used: '2021-10-31 06:49:52', - }, - ], -}; - -const empty_oauth_apps_list = { oauth_apps: [] }; - -jest.mock('@deriv/shared/src/services/ws-methods', () => ({ - __esModule: true, - default: 'mockedDefaultExport', - WS: { - authorized: { - send: ({ revoke_oauth_app }) => { - if (revoke_oauth_app) empty_oauth_apps_list; - return true_oauth_apps_list; - }, - }, - }, -})); - -describe('Connected Apps', () => { - const originalOffsetHeight = Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'offsetHeight'); - const originalOffsetWidth = Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'offsetWidth'); - - beforeAll(() => { - Object.defineProperty(HTMLElement.prototype, 'offsetHeight', { configurable: true, value: 50 }); - Object.defineProperty(HTMLElement.prototype, 'offsetWidth', { configurable: true, value: 50 }); - const modal_root_el = document.createElement('div'); - modal_root_el.setAttribute('id', 'modal_root'); - document.body.appendChild(modal_root_el); - }); - - afterAll(() => { - Object.defineProperty(HTMLElement.prototype, 'offsetHeight', originalOffsetHeight); - Object.defineProperty(HTMLElement.prototype, 'offsetWidth', originalOffsetWidth); - let modal_root_el = document.getElementById('modal_root'); - document.body.removeChild(modal_root_el); - }); - - test('renders correctly', async () => { - const { container } = render(); - - expect(screen.getByText(/Authorised applications/i)).toBeInTheDocument(); - - await waitForElementToBeRemoved(() => container.querySelector('.initial-loader')); - - await waitFor(() => { - expect(screen.getByText('Local')).toBeInTheDocument(); - expect(screen.getByText(true_oauth_apps_list.oauth_apps[0].last_used)).toBeInTheDocument(); - expect(screen.getByText('Revoke access')).toBeInTheDocument(); - }); - }); - - test('revoke access when click on confirm', async () => { - const { container } = render(); - - await waitForElementToBeRemoved(() => container.querySelector('.initial-loader')); - fireEvent.click(screen.getByText('Revoke access')); - - await waitFor(() => { - expect(screen.getByText('Confirm')).toBeInTheDocument(); - expect(screen.getByText('Back')).toBeInTheDocument(); - }); - - act(() => { - fireEvent.click(screen.getByText('Confirm')); - }); - - await waitFor(() => { - expect(screen.queryByText('Local')).not.toBeInTheDocument(); - }); - }); -}); diff --git a/packages/account/src/Sections/Security/ConnectedApps/__tests__/connected-apps.spec.tsx b/packages/account/src/Sections/Security/ConnectedApps/__tests__/connected-apps.spec.tsx new file mode 100644 index 000000000000..be6b7d141218 --- /dev/null +++ b/packages/account/src/Sections/Security/ConnectedApps/__tests__/connected-apps.spec.tsx @@ -0,0 +1,165 @@ +import React from 'react'; +import { OauthApps } from '@deriv/api-types'; +import { StoreProvider, mockStore } from '@deriv/stores'; +import { render, screen, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import ConnectedApps from '../connected-apps'; +import { WS } from '@deriv/shared'; + +const mock_connected_apps: OauthApps = [ + { + name: 'Local', + app_markup_percentage: 0, + app_id: 9999, + scopes: ['read', 'admin', 'trade', 'payments'], + last_used: '2021-10-31 06:49:52', + official: 0, + appstore: '', + github: '', + googleplay: '', + homepage: '', + redirect_uri: '', + verification_uri: '', + active: 0, + }, +]; +jest.mock('@deriv/shared', () => ({ + ...jest.requireActual('@deriv/shared'), + WS: { + authorized: { + send: jest.fn(() => ({ oauth_apps: mock_connected_apps })), + }, + }, +})); +jest.mock('@deriv/components', () => ({ + ...jest.requireActual('@deriv/components'), + Loading: jest.fn(() =>
Mocked Loading
), +})); +jest.mock('../connected-apps-earn-more', () => jest.fn(() =>
Mocked Earn More
)); +jest.mock('../connected-apps-empty', () => jest.fn(() =>
Mocked Empty Apps
)); +jest.mock('../connected-apps-know-more', () => jest.fn(() =>
Mocked Know More
)); + +describe('ConnectedApps', () => { + const originalOffsetHeight = Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'offsetHeight'); + const originalOffsetWidth = Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'offsetWidth'); + let modal_root_el: HTMLDivElement; + + beforeAll(() => { + Object.defineProperty(HTMLElement.prototype, 'offsetHeight', { configurable: true, value: 50 }); + Object.defineProperty(HTMLElement.prototype, 'offsetWidth', { configurable: true, value: 50 }); + modal_root_el = document.createElement('div'); + modal_root_el.setAttribute('id', 'modal_root'); + document.body.appendChild(modal_root_el); + }); + + afterAll(() => { + Object.defineProperty(HTMLElement.prototype, 'offsetHeight', originalOffsetHeight as PropertyDescriptor); + Object.defineProperty(HTMLElement.prototype, 'offsetWidth', originalOffsetWidth as PropertyDescriptor); + document.body.removeChild(modal_root_el); + }); + + const renderComponent = (mock_store = mockStore({})) => + render( + + + + ); + + it('should render the Loading component initially', async () => { + renderComponent(); + + expect(screen.getByText(/Mocked Loading/i)).toBeInTheDocument(); + }); + + it("should render the 'Know more' component", async () => { + renderComponent(); + + await waitFor(() => { + expect(screen.getByText(/Mocked Know More/i)).toBeInTheDocument(); + }); + }); + + it("should render the 'Earn more' component", async () => { + renderComponent(); + + await waitFor(() => { + expect(screen.getByText(/Mocked Earn More/i)).toBeInTheDocument(); + }); + }); + + it('should render the app list in Desktop view', async () => { + renderComponent(); + const mock_permissions = mock_connected_apps[0]?.scopes + ?.map(scope => scope.charAt(0).toUpperCase().concat(scope.substring(1))) + .join(', '); + + await waitFor(() => { + expect(screen.getByText('Name')).toBeInTheDocument(); + expect(screen.getByText(mock_connected_apps[0].name)).toBeInTheDocument(); + expect(screen.getByText('Last login')).toBeInTheDocument(); + if (mock_connected_apps[0]?.last_used) { + expect(screen.getByText(mock_connected_apps[0].last_used)).toBeInTheDocument(); + } else { + expect(mock_connected_apps[0].last_used).not.toBeNull(); + } + expect(screen.getByText('Permission')).toBeInTheDocument(); + if (mock_permissions) { + expect(screen.getByText(mock_permissions)).toBeInTheDocument(); + } else { + expect(mock_permissions).not.toBeNull(); + } + expect(screen.getByRole('button', { name: 'Revoke access' })).toBeInTheDocument(); + }); + }); + + it('should render the app list in Mobile view', async () => { + renderComponent(mockStore({ ui: { is_mobile: true } })); + const mock_permissions = mock_connected_apps[0]?.scopes + ?.map(scope => scope.charAt(0).toUpperCase().concat(scope.substring(1))) + .join(', '); + + await waitFor(() => { + expect(screen.getByText('Name')).toBeInTheDocument(); + expect(screen.getByText(mock_connected_apps[0].name)).toBeInTheDocument(); + expect(screen.getByText('Last login')).toBeInTheDocument(); + if (mock_connected_apps[0]?.last_used) { + expect(screen.getByText(mock_connected_apps[0].last_used)).toBeInTheDocument(); + } else { + expect(mock_connected_apps[0].last_used).not.toBeNull(); + } + expect(screen.getByText('Permission')).toBeInTheDocument(); + if (mock_permissions) { + expect(screen.getByText(mock_permissions)).toBeInTheDocument(); + } else { + expect(mock_permissions).not.toBeNull(); + } + expect(screen.getByRole('button', { name: 'Revoke access' })).toBeInTheDocument(); + }); + }); + + it('should open the modal to revoke access on clicking the button', async () => { + renderComponent(); + + await waitFor(() => { + const revoke_button = screen.getByRole('button', { name: 'Revoke access' }); + expect(revoke_button).toBeInTheDocument(); + userEvent.click(revoke_button); + expect(screen.getByText(/Confirm revoke access\?/i)).toBeInTheDocument(); + expect(screen.getByRole('button', { name: 'Back' })).toBeInTheDocument(); + + const confirm_button = screen.getByRole('button', { name: 'Confirm' }); + expect(confirm_button).toBeInTheDocument(); + userEvent.click(confirm_button); + expect(WS.authorized.send).toBeCalled(); + }); + }); + + it('should render the empty apps informative text component if there are no connected apps', async () => { + (WS.authorized.send as jest.Mock).mockReturnValue({ oauth_apps: [] }); + renderComponent(); + + await waitFor(() => { + expect(screen.getByText(/Mocked Empty Apps/i)).toBeInTheDocument(); + }); + }); +}); diff --git a/packages/account/src/Sections/Security/ConnectedApps/__tests__/data-list-template-entry.spec.tsx b/packages/account/src/Sections/Security/ConnectedApps/__tests__/data-list-template-entry.spec.tsx new file mode 100644 index 000000000000..bf9dba18ff5b --- /dev/null +++ b/packages/account/src/Sections/Security/ConnectedApps/__tests__/data-list-template-entry.spec.tsx @@ -0,0 +1,16 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import DataListTemplateEntry from '../data-list-template-entry'; + +describe('DataListTemplateEntry', () => { + it("should render the 'DataListTemplateEntry' component with correct details", () => { + const mock_props: React.ComponentProps = { + title: 'MOCK_TITLE', + content: 'MOCK_CONTENT', + }; + render(); + + expect(screen.getByText(mock_props.title.toString())).toBeInTheDocument(); + expect(screen.getByText(mock_props.content.toString())).toBeInTheDocument(); + }); +}); diff --git a/packages/account/src/Sections/Security/ConnectedApps/__tests__/data-list-template.spec.tsx b/packages/account/src/Sections/Security/ConnectedApps/__tests__/data-list-template.spec.tsx new file mode 100644 index 000000000000..22ef1f9dde91 --- /dev/null +++ b/packages/account/src/Sections/Security/ConnectedApps/__tests__/data-list-template.spec.tsx @@ -0,0 +1,41 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import DataListTemplate from '../data-list-template'; + +describe('DataListTemplate', () => { + it("should render the 'DataListTemplate' component with correct details", () => { + const mock_props: React.ComponentProps = { + data_source: { + app_id: 99, + app_markup_percentage: 1, + last_used: '2021-10-31 06:49:52', + name: 'NAME', + official: 0, + scopes: ['read', 'admin'], + appstore: null, + github: null, + googleplay: null, + homepage: null, + redirect_uri: '', + verification_uri: null, + }, + handleToggleModal: () => undefined, + }; + const mock_permissions = mock_props.data_source?.scopes + ?.map(scope => scope.charAt(0).toUpperCase().concat(scope.substring(1))) + .join(', '); + render(); + + expect(screen.getByText(mock_props.data_source.name)).toBeInTheDocument(); + if (mock_props.data_source?.last_used) { + expect(screen.getByText(mock_props.data_source?.last_used)).toBeInTheDocument(); + } else { + expect(mock_props.data_source?.last_used).not.toBeNull(); + } + if (mock_permissions) { + expect(screen.getByText(mock_permissions)).toBeInTheDocument(); + } else { + expect(mock_permissions).not.toBeNull(); + } + }); +}); diff --git a/packages/account/src/Sections/Security/ConnectedApps/connected-apps-earn-more.tsx b/packages/account/src/Sections/Security/ConnectedApps/connected-apps-earn-more.tsx new file mode 100644 index 000000000000..c2639e67a43e --- /dev/null +++ b/packages/account/src/Sections/Security/ConnectedApps/connected-apps-earn-more.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { Localize } from '@deriv/translations'; +import Article from 'Components/article'; + +const openDerivAPIWebsite = () => { + window.open('https://api.deriv.com/', '_blank', 'noopener'); +}; + +const ConnectedAppsEarnMore = () => ( +
} + descriptions={[ + , + ]} + onClickLearnMore={openDerivAPIWebsite} + /> +); + +export default ConnectedAppsEarnMore; diff --git a/packages/account/src/Sections/Security/ConnectedApps/connected-apps-empty.tsx b/packages/account/src/Sections/Security/ConnectedApps/connected-apps-empty.tsx new file mode 100644 index 000000000000..3b2c98efc12e --- /dev/null +++ b/packages/account/src/Sections/Security/ConnectedApps/connected-apps-empty.tsx @@ -0,0 +1,23 @@ +import React from 'react'; +import { Text } from '@deriv/components'; +import { observer, useStore } from '@deriv/stores'; +import { Localize } from '@deriv/translations'; +import ConnectedAppsInfoBullets from './connected-apps-info-bullets'; + +const ConnectedAppsEmpty = observer(() => { + const { ui } = useStore(); + const { is_mobile } = ui; + + const text_size = is_mobile ? 'xxs' : 'xs'; + + return ( +
+ + + + +
+ ); +}); + +export default ConnectedAppsEmpty; diff --git a/packages/account/src/Sections/Security/ConnectedApps/connected-apps-info-bullets.tsx b/packages/account/src/Sections/Security/ConnectedApps/connected-apps-info-bullets.tsx new file mode 100644 index 000000000000..9236d9d63d9b --- /dev/null +++ b/packages/account/src/Sections/Security/ConnectedApps/connected-apps-info-bullets.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import classNames from 'classnames'; +import { Text } from '@deriv/components'; +import { observer, useStore } from '@deriv/stores'; +import { CONNECTED_APPS_INFO_BULLETS } from 'Constants/connected-apps-config'; + +type TConnectedAppsInfoBulletsProps = { + class_name: string; + text_color?: string; +}; + +const ConnectedAppsInfoBullets = observer(({ class_name, text_color }: TConnectedAppsInfoBulletsProps) => { + const { ui } = useStore(); + const { is_mobile } = ui; + + const text_size = is_mobile ? 'xxxs' : 'xxs'; + + return ( + + {CONNECTED_APPS_INFO_BULLETS.map(bullet => ( +
  • {bullet.text}
  • + ))} +
    + ); +}); + +export default ConnectedAppsInfoBullets; diff --git a/packages/account/src/Sections/Security/ConnectedApps/connected-apps-info.tsx b/packages/account/src/Sections/Security/ConnectedApps/connected-apps-info.tsx new file mode 100644 index 000000000000..498bcc9efccf --- /dev/null +++ b/packages/account/src/Sections/Security/ConnectedApps/connected-apps-info.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import { InlineMessage, Text } from '@deriv/components'; +import { Localize } from '@deriv/translations'; +import { observer, useStore } from '@deriv/stores'; +import ConnectedAppsInfoBullets from './connected-apps-info-bullets'; + +const ConnectedAppsInfo = observer(() => { + const { ui } = useStore(); + const { is_mobile } = ui; + + const text_size = is_mobile ? 'xxxs' : 'xxs'; + + return ( + + + + + + + } + /> + ); +}); + +export default ConnectedAppsInfo; diff --git a/packages/account/src/Sections/Security/ConnectedApps/connected-apps-article.tsx b/packages/account/src/Sections/Security/ConnectedApps/connected-apps-know-more.tsx similarity index 63% rename from packages/account/src/Sections/Security/ConnectedApps/connected-apps-article.tsx rename to packages/account/src/Sections/Security/ConnectedApps/connected-apps-know-more.tsx index 708976db834d..32910ec875b3 100644 --- a/packages/account/src/Sections/Security/ConnectedApps/connected-apps-article.tsx +++ b/packages/account/src/Sections/Security/ConnectedApps/connected-apps-know-more.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import { localize, Localize } from '@deriv/translations'; -import AccountArticle from 'Components/article'; +import { Localize } from '@deriv/translations'; +import Article from 'Components/article'; const openAPIManagingWebsite = () => { window.open( @@ -10,10 +10,9 @@ const openAPIManagingWebsite = () => { ); }; -const ConnectedAppsArticle = () => ( - ( +
    } descriptions={[ ( /> ); -export default ConnectedAppsArticle; +export default ConnectedAppsKnowMore; diff --git a/packages/account/src/Sections/Security/ConnectedApps/connected-apps-revoke-modal.tsx b/packages/account/src/Sections/Security/ConnectedApps/connected-apps-revoke-modal.tsx new file mode 100644 index 000000000000..081fcacd1795 --- /dev/null +++ b/packages/account/src/Sections/Security/ConnectedApps/connected-apps-revoke-modal.tsx @@ -0,0 +1,38 @@ +import React from 'react'; +import { Button, Icon, Modal, Text } from '@deriv/components'; +import { Localize } from '@deriv/translations'; + +type TConnectedAppsRevokeModalProps = { + handleRevokeAccess: () => void; + handleToggleModal: (app_id?: number | null) => void; + is_modal_open: boolean; +}; + +const ConnectedAppsRevokeModal = ({ + handleRevokeAccess, + handleToggleModal, + is_modal_open, +}: TConnectedAppsRevokeModalProps) => ( + + +
    +
    + + + + +
    +
    + + +
    +
    +
    +
    +); + +export default ConnectedAppsRevokeModal; diff --git a/packages/account/src/Sections/Security/ConnectedApps/connected-apps.scss b/packages/account/src/Sections/Security/ConnectedApps/connected-apps.scss new file mode 100644 index 000000000000..ecb30795bbf4 --- /dev/null +++ b/packages/account/src/Sections/Security/ConnectedApps/connected-apps.scss @@ -0,0 +1,188 @@ +.connected-apps { + &__wrapper { + display: grid; + gap: 1.6rem; + grid-template-rows: min-content; + grid-template-columns: 1fr min-content; + @include mobile { + grid-template-columns: 1fr; + padding: 1.6rem; + } + } + + &__content { + &--wrapper { + display: flex; + flex-direction: column; + gap: 2.4rem; + @include mobile { + gap: 1.6rem; + } + } + } + + &__list { + &--wrapper { + display: flex; + flex-direction: column; + gap: 0.8rem; + } + + &--row { + display: flex; + justify-content: space-between; + gap: 1.6rem; + } + + &--template { + background: var(--general-section-1); + border-radius: $BORDER_RADIUS * 2; + padding: 1.6rem; + } + + &--name, + &--last-login, + &--permission, + &--revoke { + display: flex; + flex-direction: column; + } + + &--last-login { + width: 8rem; + } + + &--revoke { + justify-content: flex-end; + } + } + + &__tabular { + &--wrapper { + height: 36rem; + } + } + + &__articles { + &--wrapper { + display: flex; + flex-direction: column; + align-items: center; + gap: 1.6rem; + margin-left: 0.8rem; + @include mobile { + margin-left: 0; + } + + .da-article { + margin: 0; + @include mobile { + width: 100%; + } + } + } + } + + &__empty { + &--wrapper { + display: flex; + flex-direction: column; + gap: 0.8rem; + margin-top: 9.6rem; + @include mobile { + gap: 1.6rem; + margin-top: 0; + } + } + } + + &__bullets { + &--list { + list-style: auto; + display: flex; + flex-direction: column; + } + + &--with-apps { + gap: 1.4rem; + padding: 1.4rem 0 0 1.4rem; + @include mobile { + padding: 1rem 0 0 1rem; + } + } + + &--without-apps { + padding: 0 1.6rem; + gap: 0.8rem; + @include mobile { + gap: 0.4rem; + } + } + } + + // Styling for DataTable displayed in Desktop view + .table__body .table__row { + border-bottom: 1px solid var(--general-section-1); + } + + .table__head .table__row { + font-weight: bold; + } + + &__table { + height: 100%; + flex: 1; + } + + &__row { + grid-template-columns: 16rem 20rem 8rem 10rem; + padding: 0; + column-gap: 4.2rem; + text-align: left; + + .table__cell { + min-height: 5.6rem; + white-space: pre-wrap; + padding: 0; + + .name { + &__content { + text-overflow: ellipsis; + width: 100%; + overflow: hidden; + } + } + } + } +} + +.dc-modal { + &__container { + &_connected-apps { + .dc-modal { + &-body { + padding: 2.4rem; + .connected-apps-modal { + &--wrapper { + display: flex; + flex-direction: column; + align-items: center; + gap: 2.4rem; + } + + &--icon { + display: flex; + flex-direction: column; + align-items: center; + } + + &--buttons { + display: flex; + gap: 0.8rem; + } + } + } + } + } + } +} diff --git a/packages/account/src/Sections/Security/ConnectedApps/connected-apps.tsx b/packages/account/src/Sections/Security/ConnectedApps/connected-apps.tsx index 0da56348e2e5..8c9018358b22 100644 --- a/packages/account/src/Sections/Security/ConnectedApps/connected-apps.tsx +++ b/packages/account/src/Sections/Security/ConnectedApps/connected-apps.tsx @@ -1,29 +1,29 @@ import React from 'react'; -import classNames from 'classnames'; -import { - DesktopWrapper, - MobileWrapper, - Button, - Modal, - Icon, - DataTable, - DataList, - Loading, - Text, -} from '@deriv/components'; -import ConnectedAppsArticle from './connected-apps-article'; -import { PlatformContext, WS } from '@deriv/shared'; -import { localize } from '@deriv/translations'; +import { OauthApps } from '@deriv/api-types'; +import { DataTable, Loading } from '@deriv/components'; +import { observer, useStore } from '@deriv/stores'; +import { WS } from '@deriv/shared'; import ErrorComponent from 'Components/error-component'; -import GetConnectedAppsColumnsTemplate from './data-table-template'; +import ConnectedAppsKnowMore from './connected-apps-know-more'; +import ConnectedAppsInfo from './connected-apps-info'; +import ConnectedAppsEarnMore from './connected-apps-earn-more'; +import ConnectedAppsEmpty from './connected-apps-empty'; +import DataListTemplate from './data-list-template'; +import DataTableTemplate from './data-table-template'; +import ConnectedAppsRevokeModal from './connected-apps-revoke-modal'; +import './connected-apps.scss'; + +type TSource = React.ComponentProps['columns']; + +const ConnectedApps = observer(() => { + const { ui } = useStore(); + const { is_mobile } = ui; -const ConnectedApps = () => { - const { is_appstore } = React.useContext(PlatformContext); const [is_loading, setLoading] = React.useState(true); - const [is_modal_open, setModalVisibility] = React.useState(false); - const [selected_app_id, setAppId] = React.useState(null); + const [is_modal_open, setIsModalOpen] = React.useState(false); + const [selected_app_id, setSelectedAppId] = React.useState(null); const [is_error, setError] = React.useState(false); - const [connected_apps, setConnectedApps] = React.useState([]); + const [connected_apps, setConnectedApps] = React.useState([]); React.useEffect(() => { /* eslint-disable no-console */ @@ -39,40 +39,10 @@ const ConnectedApps = () => { } }; - const handleToggleModal = React.useCallback( - (app_id: number | null = null) => { - setModalVisibility(!is_modal_open); - setAppId(app_id); - }, - [is_modal_open] - ); - - type TColumn = ReturnType[number]; - - const columns_map = React.useMemo( - () => - GetConnectedAppsColumnsTemplate(app_id => handleToggleModal(app_id)).reduce((map, item) => { - map[item.col_index] = item; - return map; - }, {} as { [k in TColumn['col_index']]: TColumn }), - [handleToggleModal] - ); - - const mobileRowRenderer = React.useCallback( - ({ row }: { row: TColumn['renderCellContent'] }) => ( -
    -
    - - -
    -
    - - -
    -
    - ), - [columns_map, is_appstore] - ); + const handleToggleModal = React.useCallback((app_id: number | null = null) => { + setIsModalOpen(is_modal_open => !is_modal_open); + setSelectedAppId(app_id); + }, []); const revokeConnectedApp = React.useCallback(async (app_id: number | null) => { setLoading(true); @@ -81,83 +51,61 @@ const ConnectedApps = () => { /* eslint-disable no-console */ fetchConnectedApps().catch(error => console.error('error: ', error)); } else { + setLoading(false); setError(true); } }, []); const handleRevokeAccess = React.useCallback(() => { - setModalVisibility(false); + setIsModalOpen(false); revokeConnectedApp(selected_app_id); }, [revokeConnectedApp, selected_app_id]); - return ( -
    - - {localize('Authorised applications')} - - {is_error && } -
    - {is_loading ? ( - + return is_loading ? ( + + ) : ( +
    +
    + {is_error && } + {connected_apps.length ? ( +
    + + {is_mobile ? ( +
    + {connected_apps.map(connected_app => ( + + ))} +
    + ) : ( +
    + +
    + )} +
    ) : ( - - - - - - - - + )} - - {!is_loading && !!connected_apps.length && } -
    - - - -
    - - - {localize('Confirm revoke access?')} - -
    - - -
    -
    -
    -
    -
    + +
    + + +
    + + ); -}; +}); export default ConnectedApps; diff --git a/packages/account/src/Sections/Security/ConnectedApps/data-list-template-entry.tsx b/packages/account/src/Sections/Security/ConnectedApps/data-list-template-entry.tsx new file mode 100644 index 000000000000..961acb1673c6 --- /dev/null +++ b/packages/account/src/Sections/Security/ConnectedApps/data-list-template-entry.tsx @@ -0,0 +1,18 @@ +import React from 'react'; +import { Text } from '@deriv/components'; + +type TDataListTemplateEntry = { + title: JSX.Element | string; + content: JSX.Element | string; +}; + +const DataListTemplateEntry = ({ title, content }: TDataListTemplateEntry) => ( + + + {title} + + {content} + +); + +export default DataListTemplateEntry; diff --git a/packages/account/src/Sections/Security/ConnectedApps/data-list-template.tsx b/packages/account/src/Sections/Security/ConnectedApps/data-list-template.tsx new file mode 100644 index 000000000000..f4a6ec5fad50 --- /dev/null +++ b/packages/account/src/Sections/Security/ConnectedApps/data-list-template.tsx @@ -0,0 +1,40 @@ +import React from 'react'; +import { ApplicationObject } from '@deriv/api-types'; +import { Button } from '@deriv/components'; +import { toMoment } from '@deriv/shared'; +import { Localize } from '@deriv/translations'; +import DataListTemplateEntry from './data-list-template-entry'; +import { getConnectedAppsScopes } from './template-helper'; + +type TDataListTemplate = { data_source: ApplicationObject; handleToggleModal: (app_id: number) => void }; + +const DataListTemplate = ({ data_source, handleToggleModal }: TDataListTemplate) => ( +
    +
    +
    + } content={data_source.name} /> +
    +
    + } + content={toMoment(data_source.last_used).format('YYYY-MM-DD HH:mm:ss')} + /> +
    +
    +
    +
    + } + content={getConnectedAppsScopes(data_source.scopes ?? [])} + /> +
    +
    + +
    +
    +
    +); + +export default DataListTemplate; diff --git a/packages/account/src/Sections/Security/ConnectedApps/data-table-template.tsx b/packages/account/src/Sections/Security/ConnectedApps/data-table-template.tsx index b38f10eceece..38c8ae9b86c7 100644 --- a/packages/account/src/Sections/Security/ConnectedApps/data-table-template.tsx +++ b/packages/account/src/Sections/Security/ConnectedApps/data-table-template.tsx @@ -2,7 +2,8 @@ import React from 'react'; import { toMoment } from '@deriv/shared'; import { Button, Text } from '@deriv/components'; -import { localize } from '@deriv/translations'; +import { Localize, localize } from '@deriv/translations'; +import { getConnectedAppsScopes } from './template-helper'; type Column = { title: string; @@ -10,17 +11,11 @@ type Column = { renderCellContent: React.FC<{ cell_value: string & number & string[] }>; }; -type TGetConnectedAppsColumnsTemplate = { +type TDataTableTemplateProps = { handleToggleModal: (app_id: number | null) => void; }; -type Permissions = { - [key: string]: string; -}; - -const GetConnectedAppsColumnsTemplate = ( - handleToggleModal: TGetConnectedAppsColumnsTemplate['handleToggleModal'] -): Column[] => [ +const DataTableTemplate = ({ handleToggleModal }: TDataTableTemplateProps): Column[] => [ { title: localize('Name'), col_index: 'name', @@ -36,64 +31,31 @@ const GetConnectedAppsColumnsTemplate = ( title: localize('Permission'), col_index: 'scopes', renderCellContent: ({ cell_value }) => { - return PrepareConnectedAppsScopes(cell_value); + return getConnectedAppsScopes(cell_value); }, }, { title: localize('Last login'), col_index: 'last_used', - renderCellContent: ({ cell_value }) => PrepareConnectedAppsLastLogin(cell_value), + renderCellContent: ({ cell_value }) => getConnectedAppsLastLogin(cell_value), }, { title: localize('Action'), col_index: 'app_id', - renderCellContent: ({ cell_value }) => PrepareConnectedAppsAction(cell_value, handleToggleModal), + renderCellContent: ({ cell_value }) => getConnectedAppsAction(cell_value, handleToggleModal), }, ]; -const PrepareConnectedAppsAction = ( - app_id: number, - handleToggleModal: TGetConnectedAppsColumnsTemplate['handleToggleModal'] -) => { - return ( - - ); -}; +const getConnectedAppsAction = (app_id: number, handleToggleModal: TDataTableTemplateProps['handleToggleModal']) => ( + +); -const PrepareConnectedAppsLastLogin = (last_used: number) => ( +const getConnectedAppsLastLogin = (last_used: number) => ( {toMoment(last_used).format('YYYY-MM-DD HH:mm:ss')} ); -const generatePermissions = (): Permissions => ({ - read: localize('Read'), - trade: localize('Trade'), - trading_information: localize('Trading information'), - payments: localize('Payments'), - admin: localize('Admin'), -}); - -const PrepareConnectedAppsScopes = (permissions_list: string[]) => { - const is_trading_information = permissions_list.includes('trading_information'); - let oauth_apps_list = []; - if (is_trading_information) { - oauth_apps_list = permissions_list.filter(permission => permission !== 'trading_information'); - oauth_apps_list.push('trading_information'); - } else { - oauth_apps_list = permissions_list; - } - const sorted_app_list: string[] = []; - oauth_apps_list.forEach((permission, index) => { - if (permissions_list.length - 1 !== index) { - sorted_app_list.push(`${generatePermissions()[permission]}, `); - } else { - sorted_app_list.push(generatePermissions()[permission]); - } - }); - return
    {sorted_app_list}
    ; -}; - -export default GetConnectedAppsColumnsTemplate; +export default DataTableTemplate; diff --git a/packages/account/src/Sections/Security/ConnectedApps/index.js b/packages/account/src/Sections/Security/ConnectedApps/index.ts similarity index 100% rename from packages/account/src/Sections/Security/ConnectedApps/index.js rename to packages/account/src/Sections/Security/ConnectedApps/index.ts diff --git a/packages/account/src/Sections/Security/ConnectedApps/template-helper.tsx b/packages/account/src/Sections/Security/ConnectedApps/template-helper.tsx new file mode 100644 index 000000000000..1c0f80e92e46 --- /dev/null +++ b/packages/account/src/Sections/Security/ConnectedApps/template-helper.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import { localize } from '@deriv/translations'; + +type Permissions = { + [key: string]: string; +}; + +export const generatePermissions = (): Permissions => ({ + read: localize('Read'), + trade: localize('Trade'), + trading_information: localize('Trading information'), + payments: localize('Payments'), + admin: localize('Admin'), +}); + +export const getConnectedAppsScopes = (permissions_list: string[]) => { + const is_trading_information = permissions_list.includes('trading_information'); + let oauth_apps_list = []; + if (is_trading_information) { + oauth_apps_list = permissions_list.filter(permission => permission !== 'trading_information'); + oauth_apps_list.push('trading_information'); + } else { + oauth_apps_list = permissions_list; + } + const sorted_app_list: string[] = []; + oauth_apps_list.forEach((permission, index) => { + if (permissions_list.length - 1 !== index) { + sorted_app_list.push(`${generatePermissions()[permission]}, `); + } else { + sorted_app_list.push(generatePermissions()[permission]); + } + }); + return
    {sorted_app_list}
    ; +}; diff --git a/packages/account/src/Styles/account.scss b/packages/account/src/Styles/account.scss index 56c50c6ccaef..9463c407e159 100644 --- a/packages/account/src/Styles/account.scss +++ b/packages/account/src/Styles/account.scss @@ -1579,230 +1579,6 @@ $MIN_HEIGHT_FLOATING: calc( } } -/** @define connected-apps; weak */ -.connected-apps { - width: 100%; - height: 100%; - - .revoke_access { - padding-left: 8px; - padding-right: 8px; - left: -5%; - } - - &__loading { - height: calc(100vh - 240px); - } - - &__title { - margin-bottom: 1.6rem; - } - - &__table { - height: calc(100% - 42px); - flex: 1; - max-width: 100%; - max-height: 502px; - } - - &__row { - grid-template-columns: 169px 198px 80px 104px; - padding: 0; - column-gap: 42px; - text-align: left; - - .table__cell { - min-height: 5.6rem; - white-space: pre-wrap; - padding: 0; - - .name { - &__content { - text-overflow: ellipsis; - width: 100%; - overflow: hidden; - } - } - } - } - - &__wrapper { - max-height: 560px; - height: 100%; - width: 100%; - - &--dashboard { - max-width: 80rem; - - & .table__cell { - height: 3.8rem; - } - - & .table__row { - grid-template-columns: 17rem 20rem 15.5rem 11rem; - } - - & .data-list__item { - box-shadow: 0 1.6rem #00000005, 0 1.6rem #0000000d; - } - } - } - - &__container { - display: flex; - - @include mobile { - flex-direction: column-reverse; - } - } - - &__article { - margin-top: -4rem; - - @include mobile { - margin-top: -0.8rem; - } - } - - .table__body .table__row { - border-bottom: 1px solid var(--general-section-1); - } - - @include desktop { - &__row { - padding: 0; - } - - .table__cell { - padding: 0; - } - - .table__row { - padding: 0; - } - - .table__head { - font-weight: bold; - } - } - - @include mobile { - overflow-x: hidden; - padding: 0 1.6rem; - - /* iPhone SE screen height fixes due to UI space restrictions */ - @media only screen and (max-height: 480px) { - padding-bottom: 8.5rem; - } - - &__title { - margin: 2.4rem 0; - padding: 0 1.6rem; - } - - &__data-list { - padding: 0; - } - - &__data-list-body { - min-height: calc(100vh - 50px); - - .connected-apps { - max-height: calc(100vh - 130px); - } - - .data-list__item { - padding: 8px; - background: var(--general-section-1); - } - - .data-list__row { - display: flex; - justify-content: space-between; - padding: 0; - - &-content { - word-break: break-word; - } - - .data-list__col { - width: 50%; - margin-right: 1.8rem; - - > :first-child { - margin-bottom: 8px; - } - - &--small { - max-width: 99px; - - .data-list__row-content .dc-btn { - left: 50%; - transform: translateX(-53%); - } - - .last_used__row-title { - line-height: 1.5; - } - } - - &--dashboard { - max-width: 14.2rem; - - & .data-list__row-title { - text-align: right; - line-height: 1.5; - } - - & .data-list__row-content { - float: right; - } - - @include mobile { - display: grid; - } - } - - .data-list__row-title { - line-height: 1.5; - } - } - } - } - - &__wrapper--dashboard { - & .ReactVirtualized__Grid__innerScrollContainer { - box-shadow: 0 0 20px rgba(0, 0, 0, 0.05), 0 16px 20px rgba(0, 0, 0, 0.05); - border-radius: 0.4rem; - } - - & .data-list__col { - display: grid; - } - - & .data-list__item { - background: unset; - } - } - - .name { - &__content { - text-overflow: ellipsis; - width: 100%; - display: inline-block; - overflow: hidden; - } - } - - .last_used { - .last_used_content { - margin-bottom: 4px; - margin-top: 2px; - } - } - } -} - /** @define initial-loader; weak */ .initial-loader--btn { .initial-loader__barspinner--rect { @@ -1895,47 +1671,6 @@ $MIN_HEIGHT_FLOATING: calc( padding-top: 1.2rem; } } - - &_connected-apps { - .dc-modal-body { - padding: 0; - width: 440px; - display: flex; - justify-content: center; - - .connected-app-modal { - text-align: center; - - &__icon { - margin: 24px 0; - } - - &__confirmation { - margin-top: 24px; - margin-bottom: 32px; - - button { - min-width: 85px; - height: 40px; - } - - > :first-child { - margin-right: 16px; - } - - &-dashboard { - button { - width: unset; - } - } - } - } - - @include mobile { - width: 300px; - } - } - } } .leave-confirm { diff --git a/packages/components/src/components/icon/common/ic-account-trash-can-dashboard.svg b/packages/components/src/components/icon/common/ic-account-trash-can-dashboard.svg deleted file mode 100644 index f2953e9e3a03..000000000000 --- a/packages/components/src/components/icon/common/ic-account-trash-can-dashboard.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/components/src/components/icon/icons.js b/packages/components/src/components/icon/icons.js index 3ba72c9af31a..a2955cc71a72 100644 --- a/packages/components/src/components/icon/icons.js +++ b/packages/components/src/components/icon/icons.js @@ -249,7 +249,6 @@ import './common/ic-account-missing-details.svg'; import './common/ic-account-tick.svg'; import './common/ic-account-transfer-colored.svg'; import './common/ic-account-transfer.svg'; -import './common/ic-account-trash-can-dashboard.svg'; import './common/ic-account-trash-can.svg'; import './common/ic-account-website.svg'; import './common/ic-add-account.svg'; diff --git a/packages/components/stories/icon/icons.js b/packages/components/stories/icon/icons.js index 7bd59c50adb7..40eaf5433560 100644 --- a/packages/components/stories/icon/icons.js +++ b/packages/components/stories/icon/icons.js @@ -258,7 +258,6 @@ export const icons = 'IcAccountTick', 'IcAccountTransferColored', 'IcAccountTransfer', - 'IcAccountTrashCanDashboard', 'IcAccountTrashCan', 'IcAccountWebsite', 'IcAddAccount',