Skip to content

Commit

Permalink
Merge pull request #2234 from iFixit/match-navtab-styling
Browse files Browse the repository at this point in the history
Match Device Navigation Tab styling
  • Loading branch information
aburke07 authored Feb 8, 2024
2 parents 5e29432 + f692b5f commit 4e3c8e4
Show file tree
Hide file tree
Showing 5 changed files with 190 additions and 245 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Box, BoxProps, Flex, Link, LinkProps } from '@chakra-ui/react';
import { Box, Flex } from '@chakra-ui/react';
import { BreadCrumbs } from '@ifixit/breadcrumbs';
import type {
BreadcrumbEntry,
DeviceUrls,
} from '../hooks/useTroubleshootingProblemsProps';
import { NavTabs } from '@components/common/NavTabs';

export function NavBar({
deviceUrls,
Expand All @@ -18,8 +19,9 @@ export function NavBar({
label: breadcrumb.title,
url: breadcrumb.url,
}));
const padding = { base: 4, sm: 8 };
const padding = 4;
const breadcrumbMinHeight = '48px';
const { devicePartsUrl, deviceGuideUrl } = deviceUrls;

return (
<Flex
Expand Down Expand Up @@ -77,114 +79,25 @@ export function NavBar({
flex="1 2"
overflowX="auto"
>
<NavTabs deviceUrls={deviceUrls} />
<NavTabs
tabs={[
{
name: 'Parts',
url: devicePartsUrl,
},
{
name: 'Guides',
url: deviceGuideUrl,
},
{
name: 'Troubleshooting',
isCurrentPage: true,
},
]}
/>
</Box>
</Flex>
</Flex>
</Flex>
);
}

function NavTabs({ deviceUrls }: { deviceUrls: DeviceUrls }) {
// The type here works because all the styles we want to use are available on
// both Box and Link
const baseStyleProps: BoxProps & LinkProps = {
outline: '2px solid transparent',
outlineOffset: '2px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
paddingBlock: 2,
paddingInline: 4,
position: 'relative',
};

const bottomFeedbackStyleProps = {
content: '""',
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
height: '3px',
borderRadius: '2px 2px 0px 0px',
};

const selectedStyleProps = {
...baseStyleProps,
borderColor: 'blue.500',
color: 'gray.900',
fontWeight: 'medium',
_visited: {
color: 'gray.900',
},
_hover: {
textDecoration: 'none',
background: 'gray.100',
'::after': {
background: 'blue.700',
},
},
_after: {
...bottomFeedbackStyleProps,
background: 'blue.500',
},
};

const notSelectedStyleProps = {
...baseStyleProps,
borderColor: 'transparent',
color: 'gray.500',
fontWeight: 'normal',
_hover: {
textDecoration: 'none',
},
_visited: {
color: 'gray.500',
},
sx: {
'&:hover:not(.isDisabled)': {
color: 'gray.700',
background: 'gray.100',
},
'&.isDisabled': {
opacity: 0.4,
cursor: 'not-allowed',
color: 'gray.500',
},
},
};

const { devicePartsUrl, deviceGuideUrl } = deviceUrls;

return (
<Flex
gap={1.5}
height="100%"
overflowX="auto"
flexGrow="1"
paddingInline={{ base: 0, sm: 2 }}
>
{devicePartsUrl ? (
<Link {...notSelectedStyleProps} href={devicePartsUrl}>
Parts
</Link>
) : (
<Box className="isDisabled" {...notSelectedStyleProps}>
Parts
</Box>
)}

{deviceGuideUrl ? (
<Link {...notSelectedStyleProps} href={deviceGuideUrl}>
Guides
</Link>
) : (
<Box className="isDisabled" {...notSelectedStyleProps}>
Guides
</Box>
)}

<Box {...selectedStyleProps}>Troubleshooting</Box>
</Flex>
);
}
125 changes: 125 additions & 0 deletions frontend/components/common/NavTabs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import {
Box,
BoxProps,
Flex,
FlexProps,
Link,
LinkProps,
} from '@chakra-ui/react';

type NavTab = {
isCurrentPage?: boolean;
name: string;
url?: string;
};

export function NavTabs({
tabs,
...props
}: {
tabs: NavTab[];
} & FlexProps) {
// The type here works because all the styles we want to use are available on
// both Box and Link
const baseStyleProps: BoxProps & LinkProps = {
outline: '2px solid transparent',
outlineOffset: '2px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
paddingTop: 2,
paddingBottom: 2,
paddingInlineStart: 4,
paddingInlineEnd: 4,
position: 'relative',
fontSize: 'sm',
};

const bottomFeedbackStyleProps = {
content: '""',
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
height: '3px',
borderRadius: '2px 2px 0px 0px',
};

const selectedStyleProps = {
...baseStyleProps,
borderColor: 'blue.500',
color: 'gray.900',
fontWeight: 'medium',
_visited: {
color: 'gray.900',
},
_hover: {
textDecoration: 'none',
background: 'gray.100',
'::after': {
background: 'blue.700',
},
},
_after: {
...bottomFeedbackStyleProps,
background: 'blue.500',
},
};

const notSelectedStyleProps = {
...baseStyleProps,
borderColor: 'transparent',
color: 'gray.500',
fontWeight: 'normal',
_hover: {
textDecoration: 'none',
},
_visited: {
color: 'gray.500',
},
sx: {
'&:hover:not(.isDisabled)': {
color: 'gray.700',
background: 'gray.100',
},
'&.isDisabled': {
opacity: 0.4,
cursor: 'not-allowed',
color: 'gray.700',
background: 'gray.100',
},
},
};

return (
<Flex
overflowX="auto"
flexGrow="1"
paddingInline={{ base: 0, sm: 2 }}
gap={1.5}
height="100%"
borderLeftWidth={{ base: '0', sm: '1px', xl: '0' }}
{...props}
>
{tabs.map((tab: NavTab) =>
tab.isCurrentPage ? (
<Box key={tab.name} {...selectedStyleProps}>
{tab.name}
</Box>
) : tab.url ? (
<Link key={tab.name} {...notSelectedStyleProps} href={tab.url}>
{tab.name}
</Link>
) : (
<Box
key={tab.name}
className="isDisabled"
{...notSelectedStyleProps}
>
{tab.name}
</Box>
)
)}
</Flex>
);
}
38 changes: 18 additions & 20 deletions frontend/templates/product-list/ProductListDeviceNavigation.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Flex, FlexProps } from '@chakra-ui/react';
import { SecondaryNavbarItem, SecondaryNavbarLink } from '@components/common';
import { FlexProps } from '@chakra-ui/react';
import { IFIXIT_ORIGIN } from '@config/env';
import { stylizeDeviceTitle } from '@helpers/product-list-helpers';
import type { ProductList } from '@models/product-list';
import { ProductListType } from '@models/product-list';
import { NavTabs } from '@components/common/NavTabs';

type ProductListDeviceNavigationProps = FlexProps & {
productList: ProductList;
Expand Down Expand Up @@ -31,24 +31,22 @@ export function ProductListDeviceNavigation({
}

return (
<Flex
h="full"
align="stretch"
borderLeftWidth={{
base: '0',
sm: '1px',
md: '0',
}}
bg="white"
<NavTabs
{...flexProps}
>
<SecondaryNavbarItem isCurrent>Parts</SecondaryNavbarItem>
<SecondaryNavbarItem>
<SecondaryNavbarLink href={guideUrl}>Guides</SecondaryNavbarLink>
</SecondaryNavbarItem>
<SecondaryNavbarItem>
<SecondaryNavbarLink href={answersUrl}>Answers</SecondaryNavbarLink>
</SecondaryNavbarItem>
</Flex>
tabs={[
{
name: 'Parts',
isCurrentPage: true,
},
{
name: 'Guides',
url: guideUrl,
},
{
name: 'Answers',
url: answersUrl,
},
]}
/>
);
}
7 changes: 6 additions & 1 deletion frontend/templates/product-list/SecondaryNavigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export function SecondaryNavigation({ productList }: SecondaryNavigationProps) {
}}
breadCrumbs={breadcrumbs}
fontSize="sm"
paddingInlineEnd="16px"
/>
<Flex
h="full"
Expand All @@ -69,7 +70,11 @@ export function SecondaryNavigation({ productList }: SecondaryNavigationProps) {
<SecondaryNavbar display={{ sm: 'none' }}>
<Wrapper h="full">
<Flex h="full" w="full" boxSizing="border-box">
<BreadCrumbs breadCrumbs={breadcrumbs} fontSize="sm" />
<BreadCrumbs
breadCrumbs={breadcrumbs}
fontSize="sm"
paddingInlineEnd="16px"
/>
</Flex>
</Wrapper>
</SecondaryNavbar>
Expand Down
Loading

0 comments on commit 4e3c8e4

Please sign in to comment.