Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions src/components/NavigationTab/NavigationTab.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ export default {
page: DocumentationPage(Documentation, StyleDocs),
},
},
args: {
icon: 'chat',
},
};

const Example = Template<NavigationTabProps>(NavigationTab).bind({});
Expand All @@ -27,7 +30,7 @@ Sizes.argTypes = { ...argTypes };
delete Sizes.argTypes.size;

Sizes.parameters = {
variants: [{ size: undefined }, { size: 40 }, { size: 48 }, { size: 200 }],
variants: [{ size: undefined }, { size: 40 }, { size: 48 }, { size: 200, label: 'Chat' }],
};

const Active = MultiTemplate<NavigationTabProps>(NavigationTab).bind({});
Expand All @@ -39,4 +42,4 @@ Active.parameters = {
variants: [{ active: undefined }, { active: true }, { active: false }],
};

export { Example, Sizes };
export { Example, Sizes, Active };
5 changes: 4 additions & 1 deletion src/components/NavigationTab/NavigationTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ import './NavigationTab.style.scss';
const NavigationTab: FC<Props> = (props: Props) => {
const { icon, label, count = 0, className, id, size, style, active, ...otherProps } = props;

const isExpanded = size == 200;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this is a copy-paste, but I think our convention is always to use triple equals


const iconComponent = icon ? (
<Icon className={STYLE.icon} name={icon} scale={24} weight={'filled'} strokeColor={'none'} />
) : null;

const labelComponent =
size == 200 && label ? (
isExpanded && label ? (
<Text className={STYLE.label} type="subheader-secondary" tagName="h3">
{label}
</Text>
Expand All @@ -45,6 +47,7 @@ const NavigationTab: FC<Props> = (props: Props) => {
data-size={size || DEFAULTS.SIZE}
id={id}
style={style}
aria-label={isExpanded && label ? props['aria-label'] : props['aria-label'] || label} // if expanded with label, the accessible name will default to the visible label inside.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't get what you're doing here

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And you've not added any unit tests for it

{...otherProps}
>
{iconComponent}
Expand Down
92 changes: 50 additions & 42 deletions src/components/NavigationTab/NavigationTab.types.ts
Original file line number Diff line number Diff line change
@@ -1,47 +1,55 @@
import { CSSProperties } from 'react';
import { AriaButtonProps } from '@react-types/button';
import { AriaLabelingProps } from '@react-types/shared';
import { InferredIconName } from '../Icon/Icon.types';

export interface Props extends AriaButtonProps {
/**
* Custom class for overriding this component's CSS.
*/
className?: string;

/**
* Custom id for overriding this component's CSS.
*/
id?: string;

/**
* Custom style for overriding this component's CSS.
*/
style?: CSSProperties;

/**
* Size index of this NavTab.
*/
size?: number;

/**
* Size index of this NavTab.
*/
label?: string;

/**
* Size index of this NavTab.
*/
icon?: InferredIconName;

/**
* The amount inside the badge of components of this NavTab. If 0, then it is not shown.
*/
count?: number;

/**
* True if the tab is active.
*/
active?: boolean;
}
import { AriaLabelRequired } from '../../utils/a11y';

export type Props = AriaButtonProps &
AriaLabelingProps &
(
| {
/**
* Visible label for this tab
*/
label: string;
}
| ({ label?: never } & AriaLabelRequired)
) & {
// if a label is not provided, an aria-label(lledby) is required
/**
* Custom class for overriding this component's CSS.
*/
className?: string;

/**
* Custom id for overriding this component's CSS.
*/
id?: string;

/**
* Custom style for overriding this component's CSS.
*/
style?: CSSProperties;

/**
* Size index of this NavTab.
*/
size?: number;

/**
* Size index of this NavTab.
*/
icon?: InferredIconName;

/**
* The amount inside the badge of components of this NavTab. If 0, then it is not shown.
*/
count?: number;

/**
* True if the tab is active.
*/
active?: boolean;
};

export type NavTabSize = 40 | 48 | 200;
10 changes: 10 additions & 0 deletions src/components/NavigationTab/NavigationTab.typetest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Expect, ExpectExtends, ExpectFalse } from '../../utils/typetest.util';
import { Props } from './NavigationTab.types';

// eslint-disable-next-line @typescript-eslint/no-unused-vars
type cases = [
Expect<ExpectExtends<Props, { 'aria-label': 'label' }>>,
Expect<ExpectExtends<Props, { 'aria-labelledby': 'some-label' }>>,
Expect<ExpectExtends<Props, { label: 'Some label' }>>,
ExpectFalse<ExpectExtends<Props, Record<string, never>>>
];
49 changes: 31 additions & 18 deletions src/components/NavigationTab/NavigationTab.unit.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ describe('<NavigationTab />', () => {
it('should match snapshot', async () => {
expect.assertions(1);

const container = await mountAndWait(<NavigationTab />);
const container = await mountAndWait(<NavigationTab aria-label="chat" />);

expect(container).toMatchSnapshot();
});
Expand All @@ -21,7 +21,9 @@ describe('<NavigationTab />', () => {

const className = 'example-class';

const container = await mountAndWait(<NavigationTab className={className} />);
const container = await mountAndWait(
<NavigationTab aria-label="chat" className={className} />
);

expect(container).toMatchSnapshot();
});
Expand All @@ -31,7 +33,7 @@ describe('<NavigationTab />', () => {

const id = 'example-id';

const container = await mountAndWait(<NavigationTab id={id} />);
const container = await mountAndWait(<NavigationTab aria-label="chat" id={id} />);

expect(container).toMatchSnapshot();
});
Expand All @@ -41,7 +43,7 @@ describe('<NavigationTab />', () => {

const style = { color: 'pink' };

const container = await mountAndWait(<NavigationTab style={style} />);
const container = await mountAndWait(<NavigationTab aria-label="chat" style={style} />);

expect(container).toMatchSnapshot();
});
Expand All @@ -50,7 +52,7 @@ describe('<NavigationTab />', () => {
expect.assertions(1);

const sizes = Object.values(SIZES).map((size, index) => {
return <NavigationTab key={index} size={size as NavTabSize} />;
return <NavigationTab aria-label="chat" key={index} size={size as NavTabSize} />;
});
const container = await mountAndWait(<div>{sizes}</div>);

Expand All @@ -72,7 +74,7 @@ describe('<NavigationTab />', () => {

const icon = 'contacts';

const container = await mountAndWait(<NavigationTab icon={icon} />);
const container = await mountAndWait(<NavigationTab aria-label="chat" icon={icon} />);

expect(container).toMatchSnapshot();
});
Expand All @@ -82,7 +84,7 @@ describe('<NavigationTab />', () => {

const count = 100;

const container = await mountAndWait(<NavigationTab count={count} />);
const container = await mountAndWait(<NavigationTab aria-label="chat" count={count} />);

expect(container).toMatchSnapshot();
});
Expand All @@ -92,7 +94,7 @@ describe('<NavigationTab />', () => {

const active = true;

const container = await mountAndWait(<NavigationTab active={active} />);
const container = await mountAndWait(<NavigationTab aria-label="chat" active={active} />);

expect(container).toMatchSnapshot();
});
Expand All @@ -104,7 +106,9 @@ describe('<NavigationTab />', () => {
const label = 'Contacts';
const icon = 'contacts';

const container = await mountAndWait(<NavigationTab size={size} label={label} icon={icon} />);
const container = await mountAndWait(
<NavigationTab aria-label="chat" size={size} label={label} icon={icon} />
);

expect(container).toMatchSnapshot();
});
Expand All @@ -119,7 +123,14 @@ describe('<NavigationTab />', () => {
const active = false;

const container = await mountAndWait(
<NavigationTab size={size} label={label} icon={icon} count={count} active={active} />
<NavigationTab
aria-label="chat"
size={size}
label={label}
icon={icon}
count={count}
active={active}
/>
);

expect(container).toMatchSnapshot();
Expand All @@ -130,7 +141,7 @@ describe('<NavigationTab />', () => {
it('should have its wrapper class', async () => {
expect.assertions(1);

const wrapper = await mountAndWait(<NavigationTab />);
const wrapper = await mountAndWait(<NavigationTab aria-label="chat" />);
const element = wrapper.find(NavigationTab).getDOMNode();

expect(element.classList.contains(STYLE.wrapper)).toBe(true);
Expand All @@ -141,7 +152,7 @@ describe('<NavigationTab />', () => {

const className = 'example-class';

const wrapper = await mountAndWait(<NavigationTab className={className} />);
const wrapper = await mountAndWait(<NavigationTab aria-label="chat" className={className} />);
const element = wrapper.find(NavigationTab).getDOMNode();

expect(element.classList.contains(className)).toBe(true);
Expand All @@ -152,7 +163,7 @@ describe('<NavigationTab />', () => {

const id = 'example-id';

const wrapper = mount(<NavigationTab id={id} />);
const wrapper = mount(<NavigationTab aria-label="chat" id={id} />);
const element = wrapper.find(NavigationTab).getDOMNode();

expect(element.id).toBe(id);
Expand All @@ -164,7 +175,7 @@ describe('<NavigationTab />', () => {
const style = { color: 'pink' };
const styleString = 'color: pink;';

const wrapper = await mountAndWait(<NavigationTab style={style} />);
const wrapper = await mountAndWait(<NavigationTab aria-label="chat" style={style} />);
const element = wrapper.find(NavigationTab).getDOMNode();

expect(element.getAttribute('style')).toBe(styleString);
Expand All @@ -175,7 +186,7 @@ describe('<NavigationTab />', () => {

const size = CONSTANTS.DEFAULTS.SIZE;

const wrapper = await mountAndWait(<NavigationTab size={size} />);
const wrapper = await mountAndWait(<NavigationTab aria-label="chat" size={size} />);
const element = wrapper.find(NavigationTab).getDOMNode();

expect(element.getAttribute('data-size')).toBe(`${size}`);
Expand All @@ -199,7 +210,7 @@ describe('<NavigationTab />', () => {

const count = 1;

const wrapper = await mountAndWait(<NavigationTab count={count} />);
const wrapper = await mountAndWait(<NavigationTab aria-label="chat" count={count} />);
const element = wrapper.find(NavigationTab).getDOMNode();

const target = element.getElementsByClassName(STYLE.count)[0];
Expand All @@ -212,7 +223,7 @@ describe('<NavigationTab />', () => {

const active = CONSTANTS.DEFAULTS.ACTIVE;

const wrapper = await mountAndWait(<NavigationTab active={active} />);
const wrapper = await mountAndWait(<NavigationTab aria-label="chat" active={active} />);
const element = wrapper.find(NavigationTab).getDOMNode();

expect(element.getAttribute('data-active')).toBe(`${active}`);
Expand All @@ -225,7 +236,9 @@ describe('<NavigationTab />', () => {

const mockCallback = jest.fn();

const wrapper = await mountAndWait(<NavigationTab onPress={mockCallback} />);
const wrapper = await mountAndWait(
<NavigationTab aria-label="chat" onPress={mockCallback} />
);
const component = wrapper.find(NavigationTab);

component.props().onPress({
Expand Down
Loading
Loading