Skip to content

Commit

Permalink
Merge pull request #315 from shafin-deriv/shafin/DAPI-464/feat--api-a…
Browse files Browse the repository at this point in the history
…pplication-screen

[DAPI] feat: app manager desktop
  • Loading branch information
sandeep-deriv authored Apr 22, 2024
2 parents 07dffb8 + 0da7466 commit a8cf15c
Show file tree
Hide file tree
Showing 38 changed files with 2,169 additions and 477 deletions.
1,534 changes: 1,257 additions & 277 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"dependencies": {
"@deriv/deriv-api": "^1.0.11",
"@deriv/quill-design": "^1.2.18",
"@deriv/quill-icons": "^1.21.3",
"@deriv/ui": "^0.1.0",
"@docusaurus/core": "^2.4.0",
"@docusaurus/plugin-client-redirects": "^2.4.0",
Expand All @@ -31,8 +32,10 @@
"@easyops-cn/docusaurus-search-local": "^0.35.0",
"@hookform/resolvers": "^2.9.10",
"@mdx-js/react": "^1.6.22",
"@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-dropdown-menu": "^2.0.2",
"@radix-ui/react-tabs": "^1.0.2",
"@radix-ui/react-tooltip": "^1.0.7",
"@react-spring/web": "^9.7.3",
"@testing-library/react-hooks": "^8.0.1",
"@use-gesture/react": "^10.3.0",
Expand Down
31 changes: 31 additions & 0 deletions src/components/CustomAccordion/__tests__/custom-accordion.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from 'react';
import { cleanup, render, screen } from '@testing-library/react';
import CustomAccordion from '..';
import userEvent from '@testing-library/user-event';

const mock_accordion_items = [
{ header: 'header_1', content: 'content 1' },
{ header: 'header_2', content: 'content 2' },
];

describe('CustomAccordion', () => {
beforeEach(() => {
render(<CustomAccordion items={mock_accordion_items} />);
});

afterEach(() => {
cleanup();
});

it('should render the custom accordion', () => {
const header = screen.getByText('header_1');
expect(header).toBeInTheDocument();
});

it('should open accordion content on click', async () => {
const header = screen.getByText('header_2');
await userEvent.click(header);
const content = screen.getByText('content 2');
expect(content).toBeInTheDocument();
});
});
79 changes: 79 additions & 0 deletions src/components/CustomAccordion/custom-accordion.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
.accordion_root {
margin: 16px;
margin-top: 48px;
display: flex;
flex-direction: column;

&__item {
overflow: hidden;
margin-top: 2px;
border-radius: 24px;
}
}

.accordion_header {
display: flex;
background-color: transparent;

[data-state='open'] {
background-color: var(--opacity-black-75);
}

&__trigger {
font-family: inherit;
padding: 24px;
height: 42px;
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 16px;
line-height: 1;
}

.accordion_chevron {
transition: transform 300ms cubic-bezier(0.87, 0, 0.13, 1);
}

[data-state='open'] > .accordion_chevron {
transform: rotate(180deg);
}
}

.accordion_content {
overflow: hidden;
background-color: var(--opacity-black-75);

&__text {
padding: 16px 18px;
font-size: 14px;
font-weight: 400;
}

&[data-state='open'] {
animation: slideDown 300ms cubic-bezier(0.87, 0, 0.13, 1);
}

&[data-state='closed'] {
animation: slideUp 300ms cubic-bezier(0.87, 0, 0.13, 1);
background-color: transparent;
}
}

@keyframes slideDown {
from {
height: 0;
}
to {
height: var(--radix-accordion-content-height);
}
}

@keyframes slideUp {
from {
height: var(--radix-accordion-content-height);
}
to {
height: 0;
}
}
41 changes: 41 additions & 0 deletions src/components/CustomAccordion/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React from 'react';
import { StandaloneChevronDownRegularIcon } from '@deriv/quill-icons';
import * as Accordion from '@radix-ui/react-accordion';
import './custom-accordion.scss';

type TCustomAccordionProps = {
items: Array<{ header: string; content: React.ReactNode }>;
};

const AccordionTrigger: React.FC = ({ children }) => (
<Accordion.Header className='accordion_header'>
<Accordion.Trigger className='accordion_header__trigger'>
{children}
<StandaloneChevronDownRegularIcon iconSize='md' className='accordion_chevron' />
</Accordion.Trigger>
</Accordion.Header>
);

const AccordionContent: React.FC = ({ children }) => (
<Accordion.Content className='accordion_content'>
<div className='accordion_content__text'>{children}</div>
</Accordion.Content>
);

const CustomAccordion: React.FC<TCustomAccordionProps> = ({ items }) => (
<Accordion.Root
data-testid='dt_accordion_root'
className='accordion_root'
type='single'
collapsible
>
{items.map((item) => (
<Accordion.Item className='accordion_root__item' key={item.header} value={item.header}>
<AccordionTrigger>{item.header}</AccordionTrigger>
<AccordionContent>{item.content}</AccordionContent>
</Accordion.Item>
))}
</Accordion.Root>
);

export default CustomAccordion;
31 changes: 31 additions & 0 deletions src/components/CustomTabs/__tests__/custom-tabs.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from 'react';
import { cleanup, render, screen } from '@testing-library/react';
import CustomTabs from '..';
import userEvent from '@testing-library/user-event';

const mock_tabs = [
{ label: 'tab_1', content: 'content 1' },
{ label: 'tab_2', content: 'content 2' },
];

describe('CustomTabs', () => {
beforeEach(() => {
render(<CustomTabs tabs={mock_tabs}></CustomTabs>);
});

afterEach(() => {
cleanup();
});

it('should render the custom tabs', () => {
const tab = screen.getByText('tab_1');
expect(tab).toBeInTheDocument();
});

it('should change tab content on different tab click', async () => {
const tab = screen.getByText('tab_2');
await userEvent.click(tab);
const content = screen.getByText('content 2');
expect(content).toBeInTheDocument();
});
});
30 changes: 30 additions & 0 deletions src/components/CustomTabs/custom-tabs.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
.tabs {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;

&_header {
margin-block: 64px;
background-color: var(--opacity-black-75);
padding: 12px;
border-radius: 24px;
text-align: center;

&__items {
display: flex;
justify-content: space-between;
align-items: center;
}
&__item {
padding: 8px;
min-width: 160px;
cursor: pointer;

&.active {
background-color: var(--solid-slate-50);
border-radius: 12px;
}
}
}
}
32 changes: 32 additions & 0 deletions src/components/CustomTabs/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React, { useState } from 'react';
import './custom-tabs.scss';

const CustomTabs: React.FC<{
tabs: Array<{
label: string;
content: React.ReactNode;
}>;
}> = ({ tabs }) => {
const [activeTab, setActiveTab] = useState(0);

return (
<div className='tabs'>
<div className='tabs_header'>
<div className='tabs_header__items'>
{tabs.map((tab, index) => (
<div
key={index}
className={`tabs_header__item ${activeTab === index ? 'active' : ''}`}
onClick={() => setActiveTab(index)}
>
{tab.label}
</div>
))}
</div>
</div>
<div className='tabs_content'>{tabs[activeTab].content}</div>
</div>
);
};

export default CustomTabs;
30 changes: 30 additions & 0 deletions src/components/CustomTooltip/__tests__/custom-tooltip.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from 'react';
import { cleanup, render, screen } from '@testing-library/react';
import CustomTooltip from '..';
import userEvent from '@testing-library/user-event';

describe('CustomTooltip', () => {
beforeEach(() => {
render(
<CustomTooltip text='tooltip text'>
<div>outer text</div>
</CustomTooltip>,
);
});

afterEach(() => {
cleanup();
});

it('should render the custom tooltip with children', () => {
const text = screen.getByText('outer text');
expect(text).toBeInTheDocument();
});

it('should render the tooltip text on hover', async () => {
const text = screen.getByText('outer text');
await userEvent.hover(text);
const tooltip_text = screen.getAllByText('tooltip text');
expect(tooltip_text[0]).toBeInTheDocument();
});
});
19 changes: 19 additions & 0 deletions src/components/CustomTooltip/custom-tooltip.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.tooltip_content {
border-radius: 4px;
padding: 8px 0px;
font-size: 12px;
line-height: 14px;
color: var(--ifm-color-emphasis-100);
background-color: var(--ifm-color-emphasis-700);
box-shadow: hsl(206 22% 7% / 35%) 0px 10px 38px -10px, hsl(206 22% 7% / 20%) 0px 10px 20px -15px;
user-select: none;
animation-duration: 400ms;
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
will-change: transform, opacity;
max-width: 96px;
text-align: center;
}

.tooltip_arrow {
fill: var(--ifm-color-emphasis-700);
}
21 changes: 21 additions & 0 deletions src/components/CustomTooltip/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react';
import * as Tooltip from '@radix-ui/react-tooltip';
import './custom-tooltip.scss';

const CustomTooltip: React.FC<{ text: React.ReactNode }> = ({ children, text }) => {
return (
<Tooltip.Provider delayDuration={0}>
<Tooltip.Root>
<Tooltip.Trigger asChild>{children}</Tooltip.Trigger>
<Tooltip.Portal>
<Tooltip.Content side='bottom' className='tooltip_content'>
{text}
<Tooltip.Arrow className='tooltip_arrow' />
</Tooltip.Content>
</Tooltip.Portal>
</Tooltip.Root>
</Tooltip.Provider>
);
};

export default CustomTooltip;
2 changes: 1 addition & 1 deletion src/contexts/app-manager/app-manager.provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ type TAppManagerContextProps = {

const AppManagerContextProvider = ({ children }: TAppManagerContextProps) => {
const [apps, setApps] = useState<ApplicationObject[]>([]);
const [currentTab, setCurrentTab] = useState<TDashboardTab>('MANAGE_TOKENS');
const [currentTab, setCurrentTab] = useState<TDashboardTab>('MANAGE_APPS');
const [is_dashboard, setIsDashboard] = useState(false);
const [app_register_modal_open, setAppRegisterModalOpen] = useState(false);
const { getAllApps, apps: updatedApps } = useGetApps();
Expand Down
2 changes: 2 additions & 0 deletions src/features/dashboard/__tests__/AppManager.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const mockUseAppManager = useAppManager as jest.MockedFunction<
mockUseAppManager.mockImplementation(() => ({
setIsDashboard: jest.fn(),
getApps: jest.fn(),
updateCurrentTab: jest.fn(),
}));

jest.mock('react-table');
Expand Down Expand Up @@ -78,6 +79,7 @@ describe('AppManager', () => {
setIsDashboard: jest.fn(),
apps: [],
getApps: jest.fn(),
updateCurrentTab: jest.fn(),
}));
mockUseApiToken.mockImplementation(() => ({
tokens: [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,17 @@
padding-block: 72px;
width: 100%;

&_main {
max-width: 608px;
}

&_top {
max-width: 608px;
margin: auto;
text-align: center;
padding-inline: 16px;
h2 {
margin-bottom: 16px;
}
}

&_main {
width: 100%;
}
}
Loading

0 comments on commit a8cf15c

Please sign in to comment.