Skip to content

Commit 7b0473b

Browse files
authored
Merge pull request #482 from AppQuality/UN-557-anchor-buttons
UN-557-anchor-buttons
2 parents 49c7466 + 5e26b65 commit 7b0473b

File tree

17 files changed

+205
-31
lines changed

17 files changed

+205
-31
lines changed

src/assets/icons/linked.svg

Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import styled from 'styled-components';
2+
import { Link } from 'react-scroll';
3+
import { IconButton } from '@appquality/unguess-design-system';
4+
import { GetCampaignsByCidBugsAndBidApiResponse } from 'src/features/api';
5+
import { ReactComponent as AttachmentsIcon } from 'src/assets/icons/attachments-icon.svg';
6+
import { ReactComponent as DetailsIcon } from 'src/assets/icons/details-icon.svg';
7+
import { ReactComponent as LinkIcon } from 'src/assets/icons/linked.svg';
8+
import { useBugPreviewContext } from 'src/pages/Bugs/Content/context/BugPreviewContext';
9+
10+
const FlexComponent = styled.div`
11+
width: 100%;
12+
display: flex;
13+
justify-content: flex-start;
14+
align-items: center;
15+
margin: ${({ theme }) => theme.space.sm} 0;
16+
gap: ${({ theme }) => theme.space.sm};
17+
`;
18+
19+
export const AnchorButtons = ({
20+
bug,
21+
scrollerBoxId,
22+
}: {
23+
bug: GetCampaignsByCidBugsAndBidApiResponse;
24+
scrollerBoxId?: string;
25+
}) => {
26+
const { media } = bug;
27+
const { openAccordions, setOpenAccordions } = useBugPreviewContext();
28+
29+
return (
30+
<FlexComponent>
31+
{media && media.length && (
32+
<Link
33+
to="bug-preview-attachments"
34+
containerId={scrollerBoxId || 'main'}
35+
smooth
36+
duration={500}
37+
offset={-50}
38+
>
39+
<IconButton size="small">
40+
<AttachmentsIcon />
41+
</IconButton>
42+
</Link>
43+
)}
44+
45+
<Link
46+
to="bug-preview-details"
47+
containerId={scrollerBoxId || 'main'}
48+
smooth
49+
duration={500}
50+
offset={0}
51+
onClick={() => {
52+
setOpenAccordions([...openAccordions, 'details']);
53+
}}
54+
>
55+
<IconButton size="small">
56+
<DetailsIcon />
57+
</IconButton>
58+
</Link>
59+
60+
<Link
61+
to="bug-preview-duplicates"
62+
containerId={scrollerBoxId || 'main'}
63+
smooth
64+
duration={500}
65+
offset={0}
66+
onClick={() => {
67+
setOpenAccordions([...openAccordions, 'duplicates']);
68+
}}
69+
>
70+
<IconButton size="small">
71+
<LinkIcon />
72+
</IconButton>
73+
</Link>
74+
</FlexComponent>
75+
);
76+
};

src/common/components/BugDetail/Attachments.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export default ({ bug }: { bug: GetCampaignsByCidBugsAndBidApiResponse }) => {
5353
const extraItems = media?.filter((m) => m.mime_type.type === 'other');
5454

5555
return (
56-
<Container>
56+
<Container id="bug-preview-attachments">
5757
<Title>
5858
<AttachmentsIcon
5959
style={{

src/common/components/BugDetail/BugDuplicates/index.tsx

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Accordion, LG, Button } from '@appquality/unguess-design-system';
22
import { useState } from 'react';
33
import { Trans, useTranslation } from 'react-i18next';
44
import { theme as globalTheme } from 'src/app/theme';
5+
import { useBugPreviewContext } from 'src/pages/Bugs/Content/context/BugPreviewContext';
56
import { useSiblings } from './useSiblings';
67
import { BugDuplicatesList } from './BugDuplicatesList';
78
import { BugFather } from './BugFather';
@@ -18,9 +19,26 @@ export const BugDuplicates = ({
1819
const { t } = useTranslation();
1920
const [isOpen, setIsOpen] = useState(false);
2021
const { data } = useSiblings({ cid, bugId });
22+
const { openAccordions, setOpenAccordions } = useBugPreviewContext();
23+
const isAccordionOpen = openAccordions.includes('duplicates');
24+
25+
const handleAccordionChange = () => {
26+
if (isAccordionOpen) {
27+
setOpenAccordions(openAccordions.filter((item) => item !== 'duplicates'));
28+
} else {
29+
setOpenAccordions([...openAccordions, 'duplicates']);
30+
}
31+
};
2132

2233
return (
23-
<Accordion level={3} style={{ padding: 0 }}>
34+
<Accordion
35+
level={3}
36+
style={{ padding: 0 }}
37+
key={`duplicates_accordion_${isAccordionOpen}`}
38+
defaultExpandedSections={isAccordionOpen ? [0, 1] : []}
39+
id="bug-preview-duplicates"
40+
onChange={handleAccordionChange}
41+
>
2442
<Accordion.Section>
2543
<Accordion.Header>
2644
<Accordion.Label style={{ padding: 0 }}>

src/common/components/BugDetail/Details.tsx

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { Bug, BugAdditionalField } from 'src/features/api';
44
import styled from 'styled-components';
55
import { theme as globalTheme } from 'src/app/theme';
66
import { ReactComponent as DetailsIcon } from 'src/assets/icons/details-icon.svg';
7+
import { useBugPreviewContext } from 'src/pages/Bugs/Content/context/BugPreviewContext';
78
import DetailsItems from './DetailsItems';
89

910
const Container = styled.div`
@@ -33,10 +34,26 @@ export default ({
3334
};
3435
}) => {
3536
const { t } = useTranslation();
37+
const { openAccordions, setOpenAccordions } = useBugPreviewContext();
38+
const isOpen = openAccordions.includes('details');
39+
40+
const handleAccordionChange = () => {
41+
if (isOpen) {
42+
setOpenAccordions(openAccordions.filter((item) => item !== 'details'));
43+
} else {
44+
setOpenAccordions([...openAccordions, 'details']);
45+
}
46+
};
3647

3748
return (
38-
<Container>
39-
<Accordion level={3} style={{ padding: 0 }}>
49+
<Container id="bug-preview-details">
50+
<Accordion
51+
level={3}
52+
style={{ padding: 0 }}
53+
key={`details_accordion_${isOpen}`}
54+
defaultExpandedSections={isOpen ? [0, 1] : []}
55+
onChange={handleAccordionChange}
56+
>
4057
<Accordion.Section>
4158
<Accordion.Header>
4259
<Accordion.Label style={{ padding: 0 }}>

src/pages/Bug/Content.tsx

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import BugDescription from 'src/common/components/BugDetail/Description';
66
import BugAttachments from 'src/common/components/BugDetail/Attachments';
77
import BugDetails from 'src/common/components/BugDetail/Details';
88
import { BugDuplicates } from 'src/common/components/BugDetail/BugDuplicates';
9+
import { AnchorButtons } from 'src/common/components/BugDetail/AnchorButtons';
910
import BugHeader from './components/BugHeader';
11+
import { BugPreviewContextProvider } from '../Bugs/Content/context/BugPreviewContext';
1012

1113
interface Props {
1214
bug: Exclude<GetCampaignsByCidBugsAndBidApiResponse, undefined>;
@@ -15,14 +17,21 @@ interface Props {
1517

1618
export const Content = ({ bug, campaignId }: Props) => (
1719
<ContainerCard>
18-
<BugHeader bug={bug} />
19-
<BugMeta bug={bug} />
20-
<div style={{ width: '50%' }}>
21-
<BugTags bug={bug} campaignId={parseInt(campaignId, 10)} bugId={bug.id} />
22-
</div>
23-
<BugDescription bug={bug} />
24-
{bug.media && bug.media.length ? <BugAttachments bug={bug} /> : null}
25-
<BugDetails bug={bug} />
26-
<BugDuplicates cid={parseInt(campaignId, 10)} bugId={bug.id} />
20+
<BugPreviewContextProvider>
21+
<BugHeader bug={bug} />
22+
<BugMeta bug={bug} />
23+
<AnchorButtons bug={bug} />
24+
<div style={{ width: '50%' }}>
25+
<BugTags
26+
bug={bug}
27+
campaignId={parseInt(campaignId, 10)}
28+
bugId={bug.id}
29+
/>
30+
</div>
31+
<BugDescription bug={bug} />
32+
{bug.media && bug.media.length ? <BugAttachments bug={bug} /> : null}
33+
<BugDetails bug={bug} />
34+
<BugDuplicates cid={parseInt(campaignId, 10)} bugId={bug.id} />
35+
</BugPreviewContextProvider>
2736
</ContainerCard>
2837
);

src/pages/Bugs/Content/BugPreview.tsx

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ import { BugDuplicates } from 'src/common/components/BugDetail/BugDuplicates';
99
import { useGetCampaignsByCidBugsAndBidQuery } from 'src/features/api';
1010
import { getSelectedBugId } from 'src/features/bugsPage/bugsPageSlice';
1111
import { useEffect, useRef } from 'react';
12+
import { AnchorButtons } from 'src/common/components/BugDetail/AnchorButtons';
1213
import BugHeader from './components/BugHeader';
14+
import { BugPreviewContextProvider } from './context/BugPreviewContext';
1315

1416
const DetailContainer = styled.div`
1517
display: flex;
@@ -65,19 +67,27 @@ export const BugPreview = ({
6567
if (isLoading || isFetching || isError || !bug) return <Skeleton />;
6668

6769
const { media } = bug;
70+
const scrollerBoxId = 'bug-preview-container';
6871

6972
return (
7073
<DetailContainer>
7174
<BugHeader bug={bug} />
72-
<ScrollingContainer ref={refScroll}>
73-
<BugMeta bug={bug} />
74-
<BugTags bug={bug} campaignId={campaignId} bugId={currentBugId ?? 0} />
75-
<BugDescription bug={bug} />
76-
{media && media.length ? <BugAttachments bug={bug} /> : null}
77-
<BugDetails bug={bug} />
78-
{currentBugId && (
79-
<BugDuplicates cid={campaignId} bugId={currentBugId} />
80-
)}
75+
<ScrollingContainer ref={refScroll} id={scrollerBoxId}>
76+
<BugPreviewContextProvider>
77+
<BugMeta bug={bug} />
78+
<AnchorButtons bug={bug} scrollerBoxId={scrollerBoxId} />
79+
<BugTags
80+
bug={bug}
81+
campaignId={campaignId}
82+
bugId={currentBugId ?? 0}
83+
/>
84+
<BugDescription bug={bug} />
85+
{media && media.length ? <BugAttachments bug={bug} /> : null}
86+
<BugDetails bug={bug} />
87+
{currentBugId && (
88+
<BugDuplicates cid={campaignId} bugId={currentBugId} />
89+
)}
90+
</BugPreviewContextProvider>
8191
</ScrollingContainer>
8292
</DetailContainer>
8393
);
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { createContext, useContext, useMemo, useState } from 'react';
2+
3+
type BugAccordions = 'details' | 'duplicates';
4+
5+
interface BugPreviewContextType {
6+
openAccordions: BugAccordions[];
7+
setOpenAccordions: (accordions: BugAccordions[]) => void;
8+
}
9+
10+
const BugPreviewContext = createContext<BugPreviewContextType | null>(null);
11+
12+
export const BugPreviewContextProvider = ({
13+
children,
14+
}: {
15+
children: React.ReactNode;
16+
}) => {
17+
const [openAccordions, setOpenAccordions] = useState<BugAccordions[]>([]);
18+
19+
const bugPreviewContextValue = useMemo(
20+
() => ({
21+
openAccordions,
22+
setOpenAccordions,
23+
}),
24+
[openAccordions, setOpenAccordions]
25+
);
26+
27+
return (
28+
<BugPreviewContext.Provider value={bugPreviewContextValue}>
29+
{children}
30+
</BugPreviewContext.Provider>
31+
);
32+
};
33+
34+
export const useBugPreviewContext = () => {
35+
const context = useContext(BugPreviewContext);
36+
37+
if (!context)
38+
throw new Error('Provider not found for BugPreviewContextProvider');
39+
40+
return context; // Now we can use the context in the component, SAFELY.
41+
};

src/pages/Bugs/Drawer/DeviceField.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export const DeviceField = ({
3434

3535
return (
3636
<>
37-
<Accordion level={3}>
37+
<Accordion level={3} defaultExpandedSections={[]}>
3838
<Accordion.Section>
3939
<Accordion.Header>
4040
<Accordion.Label>

src/pages/Bugs/Drawer/OsField.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export const OsField = ({
3434

3535
return (
3636
<>
37-
<Accordion level={3}>
37+
<Accordion level={3} defaultExpandedSections={[]}>
3838
<Accordion.Section>
3939
<Accordion.Header>
4040
<Accordion.Label>

0 commit comments

Comments
 (0)