Skip to content

Commit

Permalink
Feat(web-react): Introduce UNSTABLE_Header #DS-1524
Browse files Browse the repository at this point in the history
  • Loading branch information
curdaj committed Dec 4, 2024
1 parent b3bf357 commit 24b32c6
Show file tree
Hide file tree
Showing 18 changed files with 462 additions and 0 deletions.
45 changes: 45 additions & 0 deletions packages/web-react/src/components/UNSTABLE_Header/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# UNSTABLE Header

> ⚠️ This component is UNSTABLE. It may significantly change at any point in the future.
> Please use it with caution.
```jsx
import { UNSTABLE_Header } from '@lmc-eu/spirit-web-react';

<UNSTABLE_Header>{/* Content go here */}</UNSTABLE_Header>;
```

## Header

Without any modifier, Header is ready to contain necessary blocks in a classic
left-to-right layout (in LTR documents).

```jsx
<UNSTABLE_Header>
<Container>
<Flex alignmentX="left" alignmentY="center">
<Link href="/">
<ProductLogo>
<img src="https://www.example.com/logo.png" width="65" height="24" alt="Spirit" />
</ProductLogo>
</Link>
</Flex>
</Container>
</UNSTABLE_Header>
```

## API

| Name | Type | Default | Required | Description |
| ---------- | ----------------------- | ------- | -------- | -------------------------------------------- |
| `children` | `string` \| `ReactNode` | `null` || Content of the UNSTABLE_Header |
| `isFluid` | `boolean` | `false` || UNSTABLE_Header without container |
| `isSimple` | `bool` | `false` || Shorter, centered version of UNSTABLE_Header |

The components accept [additional attributes][readme-additional-attributes].
If you need more control over the styling of a component, you can use [style props][readme-style-props]
and [escape hatches][readme-escape-hatches].

[readme-additional-attributes]: https://github.com/lmc-eu/spirit-design-system/blob/main/packages/web-react/README.md#additional-attributes
[readme-escape-hatches]: https://github.com/lmc-eu/spirit-design-system/blob/main/packages/web-react/README.md#escape-hatches
[readme-style-props]: https://github.com/lmc-eu/spirit-design-system/blob/main/packages/web-react/README.md#style-props
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
'use client';

import classNames from 'classnames';
import React from 'react';
import { useStyleProps } from '../../hooks';
import { SpiritHeaderProps } from '../../types/unstableHeader';
import { useHeaderStyleProps } from './useHeaderStyleProps';

const defaultProps: Partial<SpiritHeaderProps> = {
isFluid: false,
isSimple: false,
};

const UNSTABLE_Header = (props: SpiritHeaderProps): JSX.Element => {
const propsWithDefaults = { ...defaultProps, ...props };
const { children, ...restProps } = propsWithDefaults;

const { classProps, props: modifiedProps } = useHeaderStyleProps(restProps);
const { styleProps, props: otherProps } = useStyleProps(modifiedProps);

return (
<header {...otherProps} className={classNames(classProps.root, styleProps.className)} style={styleProps.style}>
{children}
</header>
);
};

export default UNSTABLE_Header;
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
'use client';

import classNames from 'classnames';
import React, { ElementType, forwardRef } from 'react';
import { useStyleProps } from '../../hooks';
import { PolymorphicRef, SpiritHeaderLogoProps } from '../../types';
import { useHeaderStyleProps } from './useHeaderStyleProps';

const defaultProps: Partial<SpiritHeaderLogoProps> = {
elementType: 'a',
};

/* We need an exception for components exported with forwardRef */
/* eslint no-underscore-dangle: ['error', { allow: ['_HeaderLogo'] }] */
const _HeaderLogo = <E extends ElementType = 'a'>(
props: SpiritHeaderLogoProps<E>,
ref: PolymorphicRef<E>,
): JSX.Element => {
const propsWithDefaults = { ...defaultProps, ...props };
const {
elementType: ElementTag = defaultProps.elementType as ElementType,
children,
...restProps
} = propsWithDefaults;
const { classProps, props: modifiedProps } = useHeaderStyleProps(restProps);
const { styleProps, props: otherProps } = useStyleProps(modifiedProps);

return (
<ElementTag
{...otherProps}
{...styleProps}
href={restProps.href}
className={classNames(classProps.logo, styleProps.className)}
ref={ref}
>
{children}
</ElementTag>
);
};

const UNSTABLE_HeaderLogo = forwardRef<HTMLAnchorElement, SpiritHeaderLogoProps<ElementType>>(_HeaderLogo);

export default UNSTABLE_HeaderLogo;
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import '@testing-library/jest-dom';
import { render, screen } from '@testing-library/react';
import React from 'react';
import { classNamePrefixProviderTest } from '../../../../tests/providerTests/classNamePrefixProviderTest';
import { restPropsTest } from '../../../../tests/providerTests/restPropsTest';
import { stylePropsTest } from '../../../../tests/providerTests/stylePropsTest';
import UNSTABLE_Header from '../UNSTABLE_Header';

describe('UNSTABLE_Header', () => {
classNamePrefixProviderTest(UNSTABLE_Header, 'UNSTABLE_Header');

stylePropsTest(UNSTABLE_Header);

restPropsTest(UNSTABLE_Header, 'header');

it('should have default classname', () => {
render(<UNSTABLE_Header>Content</UNSTABLE_Header>);

const header = screen.getByRole('banner');

expect(header).toHaveClass('UNSTABLE_Header');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import '@testing-library/jest-dom';
import { render, screen } from '@testing-library/react';
import React from 'react';
import { restPropsTest } from '../../../../tests/providerTests/restPropsTest';
import { stylePropsTest } from '../../../../tests/providerTests/stylePropsTest';
import UNSTABLE_HeaderLogo from '../UNSTABLE_HeaderLogo';

describe('UNSTABLE_HeaderLogo', () => {
stylePropsTest(UNSTABLE_HeaderLogo);

restPropsTest(UNSTABLE_HeaderLogo, 'a');

it('should have default classname', () => {
render(<UNSTABLE_HeaderLogo href="#">Content</UNSTABLE_HeaderLogo>);

expect(screen.getByRole('link')).toHaveClass('UNSTABLE_HeaderLogo');
});

it('should render children', () => {
render(<UNSTABLE_HeaderLogo href="#">Content</UNSTABLE_HeaderLogo>);

expect(screen.getByText('Content')).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { renderHook } from '@testing-library/react';
import { useHeaderStyleProps } from '../useHeaderStyleProps';

describe('useHeaderStyleProps', () => {
it('should return defaults', () => {
const props = {};
const { result } = renderHook(() => useHeaderStyleProps(props));

expect(result.current.classProps.root).toBe('UNSTABLE_Header');
expect(result.current.classProps.logo).toBe('UNSTABLE_HeaderLogo');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from 'react';
import { Container } from '../../Container';
import { Flex } from '../../Flex';
import { ProductLogo } from '../../ProductLogo';
import { defaultSvgLogo } from '../../ProductLogo/demo/ProductLogoDefault';
import UNSTABLE_Header from '../UNSTABLE_Header';
import UNSTABLE_HeaderLogo from '../UNSTABLE_HeaderLogo';

const HeaderDefault = () => {
return (
<UNSTABLE_Header>
<Container>
<Flex alignmentX="left" alignmentY="center">
<UNSTABLE_HeaderLogo href="/">
<ProductLogo>{defaultSvgLogo}</ProductLogo>
</UNSTABLE_HeaderLogo>
</Flex>
</Container>
</UNSTABLE_Header>
);
};
export default HeaderDefault;
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react';
import { Container } from '../../Container';
import { Flex } from '../../Flex';
import { ProductLogo } from '../../ProductLogo';
import { defaultSvgLogo } from '../../ProductLogo/demo/ProductLogoDefault';
import UNSTABLE_Header from '../UNSTABLE_Header';
import UNSTABLE_HeaderLogo from '../UNSTABLE_HeaderLogo';

const HeaderFluid = () => {
return (
<UNSTABLE_Header>
<Container isFluid>
<Flex alignmentX="left" alignmentY="center">
<UNSTABLE_HeaderLogo href="/">
<ProductLogo>{defaultSvgLogo}</ProductLogo>
</UNSTABLE_HeaderLogo>
</Flex>
</Container>
</UNSTABLE_Header>
);
};

export default HeaderFluid;
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react';
import { Flex } from '../../Flex';
import { ProductLogo } from '../../ProductLogo';
import { defaultSvgLogo } from '../../ProductLogo/demo/ProductLogoDefault';
import UNSTABLE_Header from '../UNSTABLE_Header';
import UNSTABLE_HeaderLogo from '../UNSTABLE_HeaderLogo';

const HeaderMinimal = () => {
return (
<UNSTABLE_Header>
<Flex alignmentX="center" alignmentY="center">
<UNSTABLE_HeaderLogo href="#" aria-label="JobBoard homepage">
<ProductLogo>{defaultSvgLogo}</ProductLogo>
</UNSTABLE_HeaderLogo>
</Flex>
</UNSTABLE_Header>
);
};

export default HeaderMinimal;
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import React from 'react';
import { Button } from '../../Button';
import { Container } from '../../Container';
import { Flex } from '../../Flex';
import { Icon } from '../../Icon';
import { Navigation, NavigationItem, NavigationLink } from '../../Navigation';
import { ProductLogo } from '../../ProductLogo';
import { defaultSvgLogo } from '../../ProductLogo/demo/ProductLogoDefault';
import UNSTABLE_Header from '../UNSTABLE_Header';
import UNSTABLE_HeaderLogo from '../UNSTABLE_HeaderLogo';

const HeaderDefault = () => {
return (
<UNSTABLE_Header>
<Container>
<Flex alignmentX="left" alignmentY="center" spacing="space-1000">
<UNSTABLE_HeaderLogo href="/">
<ProductLogo>{defaultSvgLogo}</ProductLogo>
</UNSTABLE_HeaderLogo>
<Navigation>
<NavigationItem>
<NavigationLink href="/">Link</NavigationLink>
</NavigationItem>
<NavigationItem>
<NavigationLink href="/" isSelected>
Selected
</NavigationLink>
</NavigationItem>
<NavigationItem>
<NavigationLink href="/" isDisabled>
Disabled
</NavigationLink>
</NavigationItem>
</Navigation>
<Navigation marginLeft="auto">
<NavigationItem>
<Button elementType="a" href="#" color="tertiary" isSymmetrical>
<Icon name="search" />
</Button>
</NavigationItem>
<NavigationItem>
<Button elementType="a" href="#" color="secondary">
Sign up
</Button>
</NavigationItem>
<NavigationItem>
<Button elementType="a" href="#">
Post a job
</Button>
</NavigationItem>
</Navigation>
</Flex>
</Container>
</UNSTABLE_Header>
);
};
export default HeaderDefault;
32 changes: 32 additions & 0 deletions packages/web-react/src/components/UNSTABLE_Header/demo/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Because there is no `dist` directory during the CI run
/* eslint-disable import/no-extraneous-dependencies, import/extensions, import/no-unresolved */
// eslint-disable-next-line @typescript-eslint/ban-ts-comment, import/extensions, import/no-unresolved
// @ts-ignore: No declaration file -- @see https://jira.almacareer.tech/browse/DS-561
import icons from '@lmc-eu/spirit-icons/icons';
import React from 'react';
import ReactDOM from 'react-dom/client';
import DocsSection from '../../../../docs/DocsSections';
import { IconsProvider } from '../../../context';
import HeaderDefault from './HeaderDefault';
import HeaderFluid from './HeaderFluid';
import HeaderMinimal from './HeaderMinimal';
import HeaderWithNavigation from './HeaderWithNavigation';

ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
<IconsProvider value={icons}>
<DocsSection title="Minimal Header" stackAlignment="stretch" container="heading-only">
<HeaderMinimal />
</DocsSection>
<DocsSection title="Full Header" stackAlignment="stretch" container="heading-only">
<HeaderDefault />
</DocsSection>
<DocsSection title="Fluid Header" stackAlignment="stretch" container="heading-only">
<HeaderFluid />
</DocsSection>
<DocsSection title="With Navigation" stackAlignment="stretch" container="heading-only">
<HeaderWithNavigation />
</DocsSection>
</IconsProvider>
</React.StrictMode>,
);
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{> web-react/demo title="Header" parentPageName="Components" isUnstable=true }}
5 changes: 5 additions & 0 deletions packages/web-react/src/components/UNSTABLE_Header/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
'use client';

export { default as UNSTABLE_Header } from './UNSTABLE_Header';
export { default as UNSTABLE_HeaderLogo } from './UNSTABLE_HeaderLogo';
export * as useUnstableHeaderStyleProps from './useHeaderStyleProps';
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Markdown } from '@storybook/blocks';
import type { Meta, StoryObj } from '@storybook/react';
import React from 'react';
import { ProductLogo } from '../../ProductLogo';
import { defaultSvgLogo } from '../../ProductLogo/demo/ProductLogoDefault';
import ReadMe from '../README.md';
import UNSTABLE_Header from '../UNSTABLE_Header';
import UNSTABLE_HeaderLogo from '../UNSTABLE_HeaderLogo';

const meta: Meta<typeof UNSTABLE_Header> = {
title: 'Experimental/UNSTABLE_Header',
component: UNSTABLE_Header,
parameters: {
docs: {
page: () => <Markdown>{ReadMe}</Markdown>,
},
},
argTypes: {
isSimple: {
control: 'boolean',
table: {
defaultValue: { summary: 'false' },
},
},
isFluid: {
control: 'boolean',
table: {
defaultValue: { summary: 'false' },
},
},
},
args: {
children: (
<UNSTABLE_HeaderLogo href="/">
<ProductLogo>{defaultSvgLogo}</ProductLogo>
</UNSTABLE_HeaderLogo>
),
isFluid: false,
isSimple: false,
},
};

export default meta;
type Story = StoryObj<typeof UNSTABLE_Header>;

export const Playground: Story = {
name: 'UNSTABLE_Header',
};
Loading

0 comments on commit 24b32c6

Please sign in to comment.