Skip to content

Commit

Permalink
add props in BPK tabs
Browse files Browse the repository at this point in the history
  • Loading branch information
Kerrie Wu committed Sep 14, 2024
1 parent 696a3bb commit 52eda97
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 37 deletions.
40 changes: 24 additions & 16 deletions examples/bpk-component-navigation-tab-group/examples.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,37 +40,38 @@ const carIcons = withRtlSupport(Car);
const flightIcons = withRtlSupport(Flight);

const tabs: BpkNavigationTabGroupProps['tabs'] = [
{ text: 'Flights', href: '/' },
{ text: 'Hotels', href: '/hotel' },
{ text: 'Car hire', href: '/carhire' },
{ text: 'Explore', href: '/Explore' },
{ id: 'air', text: 'Flights', href: '/' },
{ id: 'hotel', text: 'Hotels', href: '/hotel' },
{ id: 'car', text: 'Car hire', href: '/carhire' },
{ id: 'explore', text: 'Explore', href: '/Explore' },
];

const tabsWithIcon: BpkNavigationTabGroupProps['tabs'] = [
{ text: 'Flights', href: '/', icon: flightIcons },
{ text: 'Hotels', href: '/hotel', icon: hotelIcons },
{ text: 'Car hire', href: '/carhire', icon: carIcons },
{ text: 'Explore', href: '/Explore', icon: exploreIcons },
{ id: 'air', text: 'Flights', href: '/', icon: flightIcons, dataCy:'flight-feature', dataAnalyticsName:'flights' },
{ id: 'hotel', text: 'Hotels', href: '/hotel', icon: hotelIcons, dataCy:'hotel-feature', dataAnalyticsName:'hotels' },
{ id: 'car', text: 'Car hire', href: '/carhire', icon: carIcons, dataCy:'carhire-feature', dataAnalyticsName:'car hire' },
{ id: 'explore', text: 'Explore', href: '/Explore', icon: exploreIcons, dataCy:'explore-feature', dataAnalyticsName:'explore' },
];

const tabsNoHref: BpkNavigationTabGroupProps['tabs'] = [
{ text: 'Flights', icon: flightIcons },
{ text: 'Hotels', icon: hotelIcons },
{ text: 'Car hire', icon: carIcons },
{ text: 'Explore', icon: exploreIcons },
{ id: 'air', text: 'Flights', icon: flightIcons },
{ id: 'hotel', text: 'Hotels', icon: hotelIcons },
{ id: 'carhire', text: 'Car hire', icon: carIcons },
{ id: 'explore', text: 'Explore', icon: exploreIcons },
];

const tabsOnlyText: BpkNavigationTabGroupProps['tabs'] = [
{ text: 'Flights'},
{ text: 'Hotels' },
{ text: 'Car hire'},
{ text: 'Explore'},
{ id: 'air', text: 'Flights'},
{ id: 'hotel', text: 'Hotels' },
{ id: 'carhire', text: 'Car hire'},
{ id: 'explore', text: 'Explore'},
];

// Simple Navigation Tab Group
const SimpleSurfaceContrast = () => (
<div className={getClassNames('bpk-navigation-tab-group-story')}>
<BpkNavigationTabGroup
id='navExample'
tabs={tabs}
onItemClick={() => {}}
selectedIndex={2}
Expand All @@ -86,6 +87,7 @@ const SimpleCanvasDefault = () => (
className={getClassNames('bpk-navigation-tab-group-story__canvas-default')}
>
<BpkNavigationTabGroup
id='navExample'
tabs={tabs}
onItemClick={() => {}}
selectedIndex={0}
Expand All @@ -98,6 +100,7 @@ const SimpleCanvasDefault = () => (
const WithIconSurfaceContrastForExample = () => (
<div className={getClassNames('bpk-navigation-tab-group-story')}>
<BpkNavigationTabGroup
id='navExample'
tabs={tabsWithIcon}
onItemClick={() => {}}
selectedIndex={0}
Expand All @@ -111,6 +114,7 @@ const WithIconSurfaceContrastForExample = () => (
const WithIconCanvasDefaultForExample = () => (
<div>
<BpkNavigationTabGroup
id='navExample'
tabs={tabsWithIcon}
onItemClick={() => {}}
selectedIndex={0}
Expand All @@ -124,6 +128,7 @@ const WithIconCanvasDefaultForExample = () => (
const TabsNoHrefSurfaceContrastForExample = () => (
<div className={getClassNames('bpk-navigation-tab-group-story')}>
<BpkNavigationTabGroup
id='navExample'
tabs={tabsNoHref}
onItemClick={() => {}}
selectedIndex={0}
Expand All @@ -137,6 +142,7 @@ const TabsNoHrefSurfaceContrastForExample = () => (
const TabsNoHrefCanvasDefaultForExample = () => (
<div>
<BpkNavigationTabGroup
id='navExample'
tabs={tabsNoHref}
onItemClick={() => {}}
selectedIndex={0}
Expand All @@ -150,6 +156,7 @@ const TabsNoHrefCanvasDefaultForExample = () => (
const TabsOnlyTextSurfaceContrastForExample = () => (
<div className={getClassNames('bpk-navigation-tab-group-story')}>
<BpkNavigationTabGroup
id='navExample'
tabs={tabsOnlyText}
onItemClick={() => {}}
selectedIndex={0}
Expand All @@ -163,6 +170,7 @@ const TabsOnlyTextSurfaceContrastForExample = () => (
const TabsOnlyTextCanvasDefaultForExample = () => (
<div>
<BpkNavigationTabGroup
id='navExample'
tabs={tabsOnlyText}
onItemClick={() => {}}
selectedIndex={0}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,15 @@ import BpkNavigationTabGroup, { NAVIGATION_TAB_GROUP_TYPES } from './BpkNavigati
import type { Props } from './BpkNavigationTabGroup';

const tabs: Props['tabs'] = [
{ text: 'Flights', href: '/' },
{ text: 'Hotels', href: '/hotel' },
{ text: 'Car hire', href: '/carhire' },
{ id: 'air', text: 'Flights', href: '/' },
{ id: 'hotel', text: 'Hotels', href: '/hotel' },
{ id: 'car', text: 'Car hire', href: '/carhire' },
];

const tabsWithAnalytics: Props['tabs'] = [
{ id: 'air', text: 'Flights', href: '/', dataCy:'flight-feature', dataAnalyticsName:'flights' },
{ id: 'hotel', text: 'Hotels', href: '/hotel', dataCy:'hotel-feature', dataAnalyticsName:'hotels' },
{ id: 'car', text: 'Car hire', href: '/carhire', dataCy:'carhire-feature', dataAnalyticsName:'car hire' },
];

describe('BpkNavigationTabGroup', () => {
Expand All @@ -41,6 +47,7 @@ describe('BpkNavigationTabGroup', () => {
it('should render correctly', () => {
render(
<BpkNavigationTabGroup
id = "navTest"
tabs={tabs}
onItemClick={() => {}}
selectedIndex={0}
Expand All @@ -57,6 +64,7 @@ describe('BpkNavigationTabGroup', () => {
it('should render selected link', () => {
render(
<BpkNavigationTabGroup
id = "navTest"
tabs={tabs}
onItemClick={() => {}}
selectedIndex={0}
Expand All @@ -79,6 +87,7 @@ describe('BpkNavigationTabGroup', () => {

render(
<BpkNavigationTabGroup
id = "navTest"
tabs={tabs}
onItemClick={onItemClick}
selectedIndex={0}
Expand All @@ -90,14 +99,16 @@ describe('BpkNavigationTabGroup', () => {

expect(onItemClick).toHaveBeenCalledTimes(1);
expect(onItemClick).toHaveBeenCalledWith(
{ text: 'Hotels', href: '/hotel' },
expect.any(Object),
{ id:'hotel', text: 'Hotels', href: '/hotel' },
1,
);
});

it('should render correctly when type is CanvasDefault', () => {
render(
<BpkNavigationTabGroup
id = "navTest"
tabs={tabs}
onItemClick={() => {}}
selectedIndex={0}
Expand All @@ -111,4 +122,44 @@ describe('BpkNavigationTabGroup', () => {

expect(flightLink).toHaveClass('bpk-navigation-tab-wrap--canvas-default');
});

it('should render correctly props when tab props with data analytics', () => {
render(
<BpkNavigationTabGroup
id = "navTest"
tabs={tabsWithAnalytics}
onItemClick={() => {}}
selectedIndex={0}
type={NAVIGATION_TAB_GROUP_TYPES.CanvasDefault}
ariaLabel="Navigation tabs"
/>,
);

const flightTextElement = screen.getByText('Flights');
const flightLink = flightTextElement.closest('a');

expect(flightLink).toHaveAttribute('id', 'air');
expect(flightLink).toHaveAttribute('data-cy', 'flight-feature');
expect(flightLink).toHaveAttribute('data-analytics-name', 'flights');
});

it('should render correctly props when tab props without data analytics', () => {
render(
<BpkNavigationTabGroup
id = "navTest"
tabs={tabs}
onItemClick={() => {}}
selectedIndex={0}
type={NAVIGATION_TAB_GROUP_TYPES.CanvasDefault}
ariaLabel="Navigation tabs"
/>,
);

const flightTextElement = screen.getByText('Flights');
const flightLink = flightTextElement.closest('a');

expect(flightLink).toHaveAttribute('id', 'air');
expect(flightLink).not.toHaveAttribute('data-cy');
expect(flightLink).not.toHaveAttribute('data-analytics-name');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* limitations under the License.
*/

import type { FunctionComponent, ReactElement } from 'react';
import type { MouseEvent, FunctionComponent, ReactElement } from 'react';
import { useState } from 'react';

import BpkText, { TEXT_STYLES } from '../../bpk-component-text';
Expand All @@ -34,17 +34,21 @@ export type NavigationTabGroupTypes =
(typeof NAVIGATION_TAB_GROUP_TYPES)[keyof typeof NAVIGATION_TAB_GROUP_TYPES];

type TabItem = {
id: string;
text: string;
icon?: FunctionComponent<any> | null;
href?: string;
dataCy?: string;
dataAnalyticsName?: string;
};
export type Props = {
id: string;
tabs: TabItem[];
type?: NavigationTabGroupTypes;
/*
* Index parameter to track which is clicked
*/
onItemClick: (tab: TabItem, index: number) => void;
onItemClick: (e: MouseEvent<HTMLButtonElement | HTMLAnchorElement>,tab: TabItem, index: number) => void;
selectedIndex: number;
ariaLabel: string;
};
Expand All @@ -54,7 +58,7 @@ type TabWrapProps = {
type: NavigationTabGroupTypes;
selected: boolean;
children: ReactElement;
onClick: () => void;
onClick: (e: MouseEvent<HTMLButtonElement | HTMLAnchorElement>) => void;
};

const TabWrap = ({ children, onClick, selected, tab, type }: TabWrapProps) => {
Expand All @@ -64,22 +68,29 @@ const TabWrap = ({ children, onClick, selected, tab, type }: TabWrapProps) => {
selected && `bpk-navigation-tab-wrap--${type}-selected`,
);

const tabProps = {
...(tab.dataCy && { 'data-cy': tab.dataCy }),
...(tab.dataAnalyticsName && { 'data-analytics-name': tab.dataAnalyticsName }),
id: tab.id,
className: tabStyling,
};

return tab.href ? (
<a
className={tabStyling}
{...tabProps}
href={tab.href}
onClick={onClick}
onClick={(e: MouseEvent<HTMLAnchorElement>) => onClick(e)}
aria-current={selected ? 'page' : false}
>
{children}
</a>
) : (
<button
className={tabStyling}
{...tabProps}
type="button"
onClick={onClick}
aria-current={selected ? 'page' : false}
onClick={(e: MouseEvent<HTMLButtonElement>) => onClick(e)}
role="link"
aria-current={selected ? 'page' : false}
>
{children}
</button>
Expand All @@ -88,23 +99,25 @@ const TabWrap = ({ children, onClick, selected, tab, type }: TabWrapProps) => {

const BpkNavigationTabGroup = ({
ariaLabel,
id,
onItemClick,
selectedIndex,
tabs,
type = NAVIGATION_TAB_GROUP_TYPES.SurfaceContrast,
}: Props) => {
const [selectedTab, setSelectedTab] = useState(selectedIndex);
const handleButtonClick = (tab: TabItem, index: number) => {
const handleButtonClick = (e: MouseEvent<HTMLButtonElement | HTMLAnchorElement>, tab: TabItem, index: number) => {
if (index !== selectedTab) {
setSelectedTab(index);
}
onItemClick(tab, index);
onItemClick(e, tab, index);
};

const containerStyling = getClassName('bpk-navigation-tab-group');

return (
<nav
id={id}
className={containerStyling}
role="navigation"
aria-label={ariaLabel}
Expand All @@ -117,7 +130,7 @@ const BpkNavigationTabGroup = ({
key={`index-${index.toString()}`}
tab={tab}
selected={selected}
onClick={() => handleButtonClick(tab, index)}
onClick={(e: MouseEvent<HTMLButtonElement | HTMLAnchorElement>) => handleButtonClick(e, tab, index)}
type={type}
>
<>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,22 @@ import BpkNavigationTabGroup from './BpkNavigationTabGroup';
import type { Props } from './BpkNavigationTabGroup';

const tabs: Props['tabs'] = [
{ text: 'Flights', href: '/' },
{ text: 'Hotels', href: '/hotel' },
{ text: 'Car hire', href: '/carhire' },
{ id: 'air', text: 'Flights', href: '/' },
{ id: 'hotel',text: 'Hotels', href: '/hotel' },
{ id: 'car', text: 'Car hire', href: '/carhire' },
];

const tabsNoHref: Props['tabs'] = [
{ text: 'Flights'},
{ text: 'Hotels'},
{ text: 'Car hire'},
{ id: 'air', text: 'Flights'},
{ id: 'hotel', text: 'Hotels'},
{ id: 'car', text: 'Car hire'},
];

describe('BpkNavigationTabGroup accessibility tests', () => {
it('should not have programmatically-detectable accessibility issues', async () => {
const { container } = render(
<BpkNavigationTabGroup
id = "navTest"
tabs={tabs}
onItemClick={() => {}}
selectedIndex={0}
Expand All @@ -52,6 +53,7 @@ describe('BpkNavigationTabGroup accessibility tests', () => {
it('should not have programmatically-detectable accessibility issues without href', async () => {
const { container } = render(
<BpkNavigationTabGroup
id = "navTest"
tabs={tabsNoHref}
onItemClick={() => {}}
selectedIndex={0}
Expand Down

0 comments on commit 52eda97

Please sign in to comment.