mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Feat/add header base component (#18043)
* add header base component * fix resizing issue * add center * add demo * header base using flexbox * fix button issue * header base clean up * update tests * add readme description * add docs * update snapshot * add more to readme * convert to TS * fix file name * fix types and colors * fix classname error * fix boxprops import * fix boxprops * prop fix * fix errors * Update ui/components/component-library/header-base/header-base.stories.tsx Co-authored-by: George Marshall <george.marshall@consensys.net> * Update ui/components/component-library/header-base/header-base.types.ts Co-authored-by: George Marshall <george.marshall@consensys.net> * Update ui/components/component-library/header-base/header-base.types.ts Co-authored-by: George Marshall <george.marshall@consensys.net> * headerbase fixes * fix export * remove Math.max * change order for index on storybook to prep build * revert back to order * remove type from export * add type to export * change export of headerbase function * export update * revert back to normal * add type to export * Removing interface export from index --------- Co-authored-by: George Marshall <george.marshall@consensys.net>
This commit is contained in:
parent
e424debfc0
commit
ed5b78d61b
114
ui/components/component-library/header-base/README.mdx
Normal file
114
ui/components/component-library/header-base/README.mdx
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
import { Story, Canvas, ArgsTable } from '@storybook/addon-docs';
|
||||||
|
import { HeaderBase } from './header-base';
|
||||||
|
|
||||||
|
### This is a base component. It should not be used in your feature code directly but as a "base" for other UI components
|
||||||
|
|
||||||
|
# HeaderBase
|
||||||
|
|
||||||
|
The `HeaderBase` component is a reusable UI component for displaying a header with optional startAccessory, children (title) and endAccessory content areas. It is designed to be flexible and customizable for various use cases to keep a visually balanced appearance.
|
||||||
|
|
||||||
|
<Canvas>
|
||||||
|
<Story id="components-componentlibrary-headerbase--default-story" />
|
||||||
|
</Canvas>
|
||||||
|
|
||||||
|
## Props
|
||||||
|
|
||||||
|
The `HeaderBase` accepts all props below as well as all [Box](/docs/components-ui-box--default-story#props) component props
|
||||||
|
|
||||||
|
<ArgsTable of={HeaderBase} />
|
||||||
|
|
||||||
|
### Children
|
||||||
|
|
||||||
|
Wrapping content in the `HeaderBase` component will be rendered in the center of the header.
|
||||||
|
|
||||||
|
Use the `childrenWrapperProps` prop to customize the wrapper element around the `children` content.
|
||||||
|
|
||||||
|
<Canvas>
|
||||||
|
<Story id="components-componentlibrary-headerbase--children" />
|
||||||
|
</Canvas>
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
import { HeaderBase, Text } from '../../component-library';
|
||||||
|
import {
|
||||||
|
TEXT_ALIGN,
|
||||||
|
TextVariant,
|
||||||
|
} from '../../../helpers/constants/design-system';
|
||||||
|
|
||||||
|
<HeaderBase>
|
||||||
|
<Text variant={TextVariant.headingSm} textAlign={TEXT_ALIGN.CENTER}>
|
||||||
|
Title is sentence case no period
|
||||||
|
</Text>
|
||||||
|
</HeaderBase>;
|
||||||
|
```
|
||||||
|
|
||||||
|
### startAccessory
|
||||||
|
|
||||||
|
Using the `startAccessory` prop will render the content in the start (left) side of the header.
|
||||||
|
|
||||||
|
Use the `startAccessoryWrapperProps` prop to customize the wrapper element around the `startAccessory` content.
|
||||||
|
|
||||||
|
<Canvas>
|
||||||
|
<Story id="components-componentlibrary-headerbase--start-accessory" />
|
||||||
|
</Canvas>
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
import { HeaderBase, Text } from '../../component-library';
|
||||||
|
import {
|
||||||
|
TEXT_ALIGN,
|
||||||
|
TextVariant,
|
||||||
|
} from '../../../helpers/constants/design-system';
|
||||||
|
|
||||||
|
<HeaderBase
|
||||||
|
startAccessory={
|
||||||
|
<ButtonIcon
|
||||||
|
size={BUTTON_ICON_SIZES.SM}
|
||||||
|
iconName={ICON_NAMES.ARROW_LEFT}
|
||||||
|
ariaLabel="back"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Text variant={TextVariant.headingSm} textAlign={TEXT_ALIGN.CENTER}>
|
||||||
|
Title is sentence case no period
|
||||||
|
</Text>
|
||||||
|
</HeaderBase>;
|
||||||
|
```
|
||||||
|
|
||||||
|
### endAccessory
|
||||||
|
|
||||||
|
Using the `endAccessory` prop will render the content in the end (right) side of the header.
|
||||||
|
|
||||||
|
Use the `endAccessoryWrapperProps` prop to customize the wrapper element around the `endAccessory` content.
|
||||||
|
|
||||||
|
<Canvas>
|
||||||
|
<Story id="components-componentlibrary-headerbase--end-accessory" />
|
||||||
|
</Canvas>
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
import { HeaderBase, Text } from '../../component-library';
|
||||||
|
import {
|
||||||
|
TEXT_ALIGN,
|
||||||
|
TextVariant,
|
||||||
|
} from '../../../helpers/constants/design-system';
|
||||||
|
|
||||||
|
<HeaderBase
|
||||||
|
endAccessory={
|
||||||
|
<ButtonIcon
|
||||||
|
size={BUTTON_ICON_SIZES.SM}
|
||||||
|
iconName={ICON_NAMES.CLOSE}
|
||||||
|
ariaLabel="close"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Text variant={TextVariant.headingSm} textAlign={TEXT_ALIGN.CENTER}>
|
||||||
|
Title is sentence case no period
|
||||||
|
</Text>
|
||||||
|
</HeaderBase>;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Use Case Demos
|
||||||
|
|
||||||
|
Some examples of how the `HeaderBase` component can be used in various use cases with background colors set for visual aid.
|
||||||
|
|
||||||
|
<Canvas>
|
||||||
|
<Story id="components-componentlibrary-headerbase--use-case-demos" />
|
||||||
|
</Canvas>
|
@ -0,0 +1,17 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`HeaderBase should render HeaderBase element correctly 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="box mm-header-base box--display-flex box--flex-direction-row box--justify-content-space-between"
|
||||||
|
data-testid="header-base"
|
||||||
|
title="HeaderBase test"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="box mm-header-base__children box--flex-direction-row box--width-full"
|
||||||
|
>
|
||||||
|
should render HeaderBase element correctly
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
@ -0,0 +1,293 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
||||||
|
import Box from '../../ui/box';
|
||||||
|
import {
|
||||||
|
ICON_NAMES,
|
||||||
|
Button,
|
||||||
|
ButtonIcon,
|
||||||
|
BUTTON_ICON_SIZES,
|
||||||
|
BUTTON_SIZES,
|
||||||
|
Text,
|
||||||
|
} from '..';
|
||||||
|
import {
|
||||||
|
AlignItems,
|
||||||
|
BackgroundColor,
|
||||||
|
TextVariant,
|
||||||
|
TEXT_ALIGN,
|
||||||
|
} from '../../../helpers/constants/design-system';
|
||||||
|
import { HeaderBase } from './header-base';
|
||||||
|
import README from './README.mdx';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Components/ComponentLibrary/HeaderBase',
|
||||||
|
component: HeaderBase,
|
||||||
|
parameters: {
|
||||||
|
docs: {
|
||||||
|
page: README,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as ComponentMeta<typeof HeaderBase>;
|
||||||
|
|
||||||
|
const Template: ComponentStory<typeof HeaderBase> = (args) => (
|
||||||
|
<HeaderBase {...args} />
|
||||||
|
);
|
||||||
|
|
||||||
|
export const DefaultStory = Template.bind({});
|
||||||
|
|
||||||
|
DefaultStory.args = {
|
||||||
|
children: (
|
||||||
|
<Text variant={TextVariant.headingSm} textAlign={TEXT_ALIGN.CENTER}>
|
||||||
|
Title is sentence case no period
|
||||||
|
</Text>
|
||||||
|
),
|
||||||
|
startAccessory: (
|
||||||
|
<ButtonIcon
|
||||||
|
size={BUTTON_ICON_SIZES.SM}
|
||||||
|
iconName={ICON_NAMES.ARROW_LEFT}
|
||||||
|
ariaLabel="back"
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
endAccessory: (
|
||||||
|
<ButtonIcon
|
||||||
|
size={BUTTON_ICON_SIZES.SM}
|
||||||
|
iconName={ICON_NAMES.CLOSE}
|
||||||
|
ariaLabel="close"
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
DefaultStory.storyName = 'Default';
|
||||||
|
|
||||||
|
export const Children = (args) => {
|
||||||
|
return (
|
||||||
|
<HeaderBase {...args}>
|
||||||
|
<Text variant={TextVariant.headingSm} textAlign={TEXT_ALIGN.CENTER}>
|
||||||
|
Title is sentence case no period
|
||||||
|
</Text>
|
||||||
|
</HeaderBase>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const StartAccessory = (args) => {
|
||||||
|
return (
|
||||||
|
<HeaderBase
|
||||||
|
marginBottom={4}
|
||||||
|
startAccessory={
|
||||||
|
<ButtonIcon
|
||||||
|
size={BUTTON_ICON_SIZES.SM}
|
||||||
|
iconName={ICON_NAMES.ARROW_LEFT}
|
||||||
|
ariaLabel="back"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
{...args}
|
||||||
|
>
|
||||||
|
<Text variant={TextVariant.headingSm} textAlign={TEXT_ALIGN.CENTER}>
|
||||||
|
Title is sentence case no period
|
||||||
|
</Text>
|
||||||
|
</HeaderBase>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const EndAccessory = (args) => {
|
||||||
|
return (
|
||||||
|
<HeaderBase
|
||||||
|
marginBottom={4}
|
||||||
|
endAccessory={
|
||||||
|
<ButtonIcon
|
||||||
|
size={BUTTON_ICON_SIZES.SM}
|
||||||
|
iconName={ICON_NAMES.CLOSE}
|
||||||
|
ariaLabel="close"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
{...args}
|
||||||
|
>
|
||||||
|
<Text variant={TextVariant.headingSm} textAlign={TEXT_ALIGN.CENTER}>
|
||||||
|
Title is sentence case no period
|
||||||
|
</Text>
|
||||||
|
</HeaderBase>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const UseCaseDemos = (args) => (
|
||||||
|
<>
|
||||||
|
<Text>children only assigned</Text>
|
||||||
|
<Box backgroundColor={BackgroundColor.warningAlternative}>
|
||||||
|
<HeaderBase marginBottom={4} {...args}>
|
||||||
|
<Text
|
||||||
|
variant={TextVariant.headingSm}
|
||||||
|
textAlign={TEXT_ALIGN.CENTER}
|
||||||
|
backgroundColor={BackgroundColor.primaryAlternative}
|
||||||
|
>
|
||||||
|
Title is sentence case no period
|
||||||
|
</Text>
|
||||||
|
</HeaderBase>
|
||||||
|
</Box>
|
||||||
|
<Text>children and endAccessory assigned </Text>
|
||||||
|
<Box backgroundColor={BackgroundColor.warningAlternative}>
|
||||||
|
<HeaderBase
|
||||||
|
marginBottom={4}
|
||||||
|
endAccessory={
|
||||||
|
<ButtonIcon
|
||||||
|
backgroundColor={BackgroundColor.goerli}
|
||||||
|
size={BUTTON_ICON_SIZES.SM}
|
||||||
|
iconName={ICON_NAMES.CLOSE}
|
||||||
|
ariaLabel="close"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
{...args}
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
variant={TextVariant.headingSm}
|
||||||
|
textAlign={TEXT_ALIGN.CENTER}
|
||||||
|
backgroundColor={BackgroundColor.primaryAlternative}
|
||||||
|
>
|
||||||
|
Title is sentence case no period
|
||||||
|
</Text>
|
||||||
|
</HeaderBase>
|
||||||
|
</Box>
|
||||||
|
<Text>children and startAccessory assigned </Text>
|
||||||
|
<Box backgroundColor={BackgroundColor.warningAlternative}>
|
||||||
|
<HeaderBase
|
||||||
|
marginBottom={4}
|
||||||
|
startAccessory={
|
||||||
|
<ButtonIcon
|
||||||
|
backgroundColor={BackgroundColor.successAlternative}
|
||||||
|
size={BUTTON_ICON_SIZES.SM}
|
||||||
|
iconName={ICON_NAMES.ARROW_LEFT}
|
||||||
|
ariaLabel="back"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
{...args}
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
variant={TextVariant.headingSm}
|
||||||
|
textAlign={TEXT_ALIGN.CENTER}
|
||||||
|
backgroundColor={BackgroundColor.primaryAlternative}
|
||||||
|
>
|
||||||
|
Title is sentence case no period
|
||||||
|
</Text>
|
||||||
|
</HeaderBase>
|
||||||
|
</Box>
|
||||||
|
<Text>children, startAccessory, and endAccessory assigned </Text>
|
||||||
|
<Box backgroundColor={BackgroundColor.warningAlternative}>
|
||||||
|
<HeaderBase
|
||||||
|
marginBottom={4}
|
||||||
|
startAccessory={
|
||||||
|
<ButtonIcon
|
||||||
|
backgroundColor={BackgroundColor.successAlternative}
|
||||||
|
size={BUTTON_ICON_SIZES.SM}
|
||||||
|
iconName={ICON_NAMES.ARROW_LEFT}
|
||||||
|
ariaLabel="back"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
endAccessory={
|
||||||
|
<ButtonIcon
|
||||||
|
backgroundColor={BackgroundColor.goerli}
|
||||||
|
size={BUTTON_ICON_SIZES.SM}
|
||||||
|
iconName={ICON_NAMES.CLOSE}
|
||||||
|
ariaLabel="close"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
{...args}
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
variant={TextVariant.headingSm}
|
||||||
|
textAlign={TEXT_ALIGN.CENTER}
|
||||||
|
backgroundColor={BackgroundColor.primaryAlternative}
|
||||||
|
>
|
||||||
|
Title is sentence case no period
|
||||||
|
</Text>
|
||||||
|
</HeaderBase>
|
||||||
|
</Box>
|
||||||
|
<Text>children, startAccessory, and endAccessory assigned </Text>
|
||||||
|
<Box backgroundColor={BackgroundColor.warningAlternative}>
|
||||||
|
<HeaderBase
|
||||||
|
marginBottom={4}
|
||||||
|
startAccessory={
|
||||||
|
<Button
|
||||||
|
backgroundColor={BackgroundColor.successAlternative}
|
||||||
|
style={{ whiteSpace: 'nowrap' }}
|
||||||
|
size={BUTTON_SIZES.SM}
|
||||||
|
>
|
||||||
|
Unlock Now
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
endAccessory={
|
||||||
|
<ButtonIcon
|
||||||
|
backgroundColor={BackgroundColor.goerli}
|
||||||
|
size={BUTTON_ICON_SIZES.SM}
|
||||||
|
iconName={ICON_NAMES.CLOSE}
|
||||||
|
ariaLabel="close"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
{...args}
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
variant={TextVariant.headingSm}
|
||||||
|
textAlign={TEXT_ALIGN.CENTER}
|
||||||
|
backgroundColor={BackgroundColor.primaryAlternative}
|
||||||
|
>
|
||||||
|
Title is sentence case no period
|
||||||
|
</Text>
|
||||||
|
</HeaderBase>
|
||||||
|
</Box>
|
||||||
|
<Text>
|
||||||
|
children, startAccessory, and endAccessory assigned with prop alignItems=
|
||||||
|
{AlignItems.center} passed at HeaderBase
|
||||||
|
</Text>
|
||||||
|
<Box backgroundColor={BackgroundColor.warningAlternative}>
|
||||||
|
<HeaderBase
|
||||||
|
marginBottom={4}
|
||||||
|
alignItems={AlignItems.center}
|
||||||
|
startAccessory={
|
||||||
|
<ButtonIcon
|
||||||
|
backgroundColor={BackgroundColor.successAlternative}
|
||||||
|
size={BUTTON_ICON_SIZES.SM}
|
||||||
|
iconName={ICON_NAMES.CLOSE}
|
||||||
|
ariaLabel="close"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
endAccessory={
|
||||||
|
<Button
|
||||||
|
backgroundColor={BackgroundColor.goerli}
|
||||||
|
size={BUTTON_SIZES.SM}
|
||||||
|
>
|
||||||
|
Download
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
{...args}
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
variant={TextVariant.headingSm}
|
||||||
|
textAlign={TEXT_ALIGN.CENTER}
|
||||||
|
backgroundColor={BackgroundColor.primaryAlternative}
|
||||||
|
>
|
||||||
|
Title is sentence case no period
|
||||||
|
</Text>
|
||||||
|
</HeaderBase>
|
||||||
|
</Box>
|
||||||
|
<Text>startAccessory and endAccessory assigned </Text>
|
||||||
|
<Box backgroundColor={BackgroundColor.warningAlternative}>
|
||||||
|
<HeaderBase
|
||||||
|
marginBottom={4}
|
||||||
|
startAccessory={
|
||||||
|
<Button
|
||||||
|
backgroundColor={BackgroundColor.successAlternative}
|
||||||
|
size={BUTTON_SIZES.SM}
|
||||||
|
>
|
||||||
|
Unlock
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
endAccessory={
|
||||||
|
<ButtonIcon
|
||||||
|
backgroundColor={BackgroundColor.goerli}
|
||||||
|
size={BUTTON_ICON_SIZES.SM}
|
||||||
|
iconName={ICON_NAMES.CLOSE}
|
||||||
|
ariaLabel="close"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
{...args}
|
||||||
|
></HeaderBase>
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
);
|
@ -0,0 +1,61 @@
|
|||||||
|
/* eslint-disable jest/require-top-level-describe */
|
||||||
|
import { render } from '@testing-library/react';
|
||||||
|
import React from 'react';
|
||||||
|
import { Icon, ICON_NAMES } from '..';
|
||||||
|
import { HeaderBase } from './header-base';
|
||||||
|
|
||||||
|
describe('HeaderBase', () => {
|
||||||
|
it('should render HeaderBase element correctly', () => {
|
||||||
|
const { getByTestId, container } = render(
|
||||||
|
<HeaderBase data-testid="header-base" title="HeaderBase test">
|
||||||
|
should render HeaderBase element correctly
|
||||||
|
</HeaderBase>,
|
||||||
|
);
|
||||||
|
expect(getByTestId('header-base')).toHaveClass('mm-header-base');
|
||||||
|
expect(container).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render with added classname', () => {
|
||||||
|
const { getByTestId } = render(
|
||||||
|
<HeaderBase
|
||||||
|
className="mm-header-base--test"
|
||||||
|
data-testid="header-base"
|
||||||
|
title="HeaderBase test"
|
||||||
|
>
|
||||||
|
should render HeaderBase element correctly
|
||||||
|
</HeaderBase>,
|
||||||
|
);
|
||||||
|
expect(getByTestId('header-base')).toHaveClass('mm-header-base--test');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render HeaderBase children', () => {
|
||||||
|
const { getByText } = render(
|
||||||
|
<HeaderBase>HeaderBase children test</HeaderBase>,
|
||||||
|
);
|
||||||
|
expect(getByText('HeaderBase children test')).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render HeaderBase startAccessory', () => {
|
||||||
|
const { getByTestId } = render(
|
||||||
|
<HeaderBase
|
||||||
|
startAccessory={
|
||||||
|
<Icon data-testid="start-accessory" name={ICON_NAMES.ADD_SQUARE} />
|
||||||
|
}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(getByTestId('start-accessory')).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render HeaderBase endAccessory', () => {
|
||||||
|
const { getByTestId } = render(
|
||||||
|
<HeaderBase
|
||||||
|
endAccessory={
|
||||||
|
<Icon data-testid="end-accessory" name={ICON_NAMES.ADD_SQUARE} />
|
||||||
|
}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(getByTestId('end-accessory')).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
117
ui/components/component-library/header-base/header-base.tsx
Normal file
117
ui/components/component-library/header-base/header-base.tsx
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
import React, { useRef, useLayoutEffect, useMemo, useState } from 'react';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
import {
|
||||||
|
BLOCK_SIZES,
|
||||||
|
DISPLAY,
|
||||||
|
JustifyContent,
|
||||||
|
} from '../../../helpers/constants/design-system';
|
||||||
|
import Box from '../../ui/box';
|
||||||
|
|
||||||
|
import { HeaderBaseProps } from './header-base.types';
|
||||||
|
|
||||||
|
export const HeaderBase: React.FC<HeaderBaseProps> = ({
|
||||||
|
startAccessory,
|
||||||
|
endAccessory,
|
||||||
|
className = '',
|
||||||
|
children,
|
||||||
|
childrenWrapperProps,
|
||||||
|
startAccessoryWrapperProps,
|
||||||
|
endAccessoryWrapperProps,
|
||||||
|
...props
|
||||||
|
}) => {
|
||||||
|
const startAccessoryRef = useRef<HTMLDivElement>(null);
|
||||||
|
const endAccessoryRef = useRef<HTMLDivElement>(null);
|
||||||
|
const [accessoryMinWidth, setAccessoryMinWidth] = useState<number>();
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
function handleResize() {
|
||||||
|
if (startAccessoryRef.current && endAccessoryRef.current) {
|
||||||
|
const accMinWidth = Math.max(
|
||||||
|
startAccessoryRef.current.scrollWidth,
|
||||||
|
endAccessoryRef.current.scrollWidth,
|
||||||
|
);
|
||||||
|
setAccessoryMinWidth(accMinWidth);
|
||||||
|
} else if (startAccessoryRef.current && !endAccessoryRef.current) {
|
||||||
|
setAccessoryMinWidth(startAccessoryRef.current.scrollWidth);
|
||||||
|
} else if (!startAccessoryRef.current && endAccessoryRef.current) {
|
||||||
|
setAccessoryMinWidth(endAccessoryRef.current.scrollWidth);
|
||||||
|
} else {
|
||||||
|
setAccessoryMinWidth(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleResize();
|
||||||
|
window.addEventListener('resize', handleResize);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('resize', handleResize);
|
||||||
|
};
|
||||||
|
}, [startAccessoryRef, endAccessoryRef, children]);
|
||||||
|
|
||||||
|
const getTitleStyles = useMemo(() => {
|
||||||
|
if (startAccessory && !endAccessory) {
|
||||||
|
return {
|
||||||
|
marginRight: `${accessoryMinWidth}px`,
|
||||||
|
};
|
||||||
|
} else if (!startAccessory && endAccessory) {
|
||||||
|
return {
|
||||||
|
marginLeft: `${accessoryMinWidth}px`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}, [accessoryMinWidth, startAccessory, endAccessory]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
className={classnames('mm-header-base', className)}
|
||||||
|
display={DISPLAY.FLEX}
|
||||||
|
justifyContent={JustifyContent.spaceBetween}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{startAccessory && (
|
||||||
|
<Box
|
||||||
|
className="mm-header-base__start-accessory"
|
||||||
|
ref={startAccessoryRef}
|
||||||
|
style={
|
||||||
|
children
|
||||||
|
? {
|
||||||
|
minWidth: `${accessoryMinWidth}px`,
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
{...startAccessoryWrapperProps}
|
||||||
|
>
|
||||||
|
{startAccessory}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
{children && (
|
||||||
|
<Box
|
||||||
|
className="mm-header-base__children"
|
||||||
|
width={BLOCK_SIZES.FULL}
|
||||||
|
style={getTitleStyles}
|
||||||
|
{...childrenWrapperProps}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
{endAccessory && (
|
||||||
|
<Box
|
||||||
|
display={DISPLAY.FLEX}
|
||||||
|
justifyContent={JustifyContent.flexEnd}
|
||||||
|
className="mm-header-base__end-accessory"
|
||||||
|
ref={endAccessoryRef}
|
||||||
|
style={
|
||||||
|
children
|
||||||
|
? {
|
||||||
|
minWidth: `${accessoryMinWidth}px`,
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
{...endAccessoryWrapperProps}
|
||||||
|
>
|
||||||
|
{endAccessory}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,33 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import type { BoxProps } from '../../ui/box/box.d';
|
||||||
|
|
||||||
|
export interface HeaderBaseProps extends BoxProps {
|
||||||
|
/**
|
||||||
|
* The children is the title area of the HeaderBase
|
||||||
|
*/
|
||||||
|
children?: React.ReactNode;
|
||||||
|
/**
|
||||||
|
* Use the `childrenWrapperProps` prop to define the props to the children wrapper
|
||||||
|
*/
|
||||||
|
childrenWrapperProps?: BoxProps;
|
||||||
|
/**
|
||||||
|
* The start(default left) content area of HeaderBase
|
||||||
|
*/
|
||||||
|
startAccessory?: React.ReactNode;
|
||||||
|
/**
|
||||||
|
* Use the `startAccessoryWrapperProps` prop to define the props to the start accessory wrapper
|
||||||
|
*/
|
||||||
|
startAccessoryWrapperProps?: BoxProps;
|
||||||
|
/**
|
||||||
|
* The end (default right) content area of HeaderBase
|
||||||
|
*/
|
||||||
|
endAccessory?: React.ReactNode;
|
||||||
|
/**
|
||||||
|
* Use the `endAccessoryWrapperProps` prop to define the props to the end accessory wrapper
|
||||||
|
*/
|
||||||
|
endAccessoryWrapperProps?: BoxProps;
|
||||||
|
/**
|
||||||
|
* An additional className to apply to the HeaderBase
|
||||||
|
*/
|
||||||
|
className?: string;
|
||||||
|
}
|
2
ui/components/component-library/header-base/index.ts
Normal file
2
ui/components/component-library/header-base/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export { HeaderBase } from './header-base';
|
||||||
|
export type { HeaderBaseProps } from './header-base.types';
|
@ -21,6 +21,7 @@ export { ButtonLink, BUTTON_LINK_SIZES } from './button-link';
|
|||||||
export { ButtonPrimary, BUTTON_PRIMARY_SIZES } from './button-primary';
|
export { ButtonPrimary, BUTTON_PRIMARY_SIZES } from './button-primary';
|
||||||
export { ButtonSecondary, BUTTON_SECONDARY_SIZES } from './button-secondary';
|
export { ButtonSecondary, BUTTON_SECONDARY_SIZES } from './button-secondary';
|
||||||
export { FormTextField } from './form-text-field';
|
export { FormTextField } from './form-text-field';
|
||||||
|
export { HeaderBase } from './header-base';
|
||||||
export { HelpText } from './help-text';
|
export { HelpText } from './help-text';
|
||||||
export { Icon, ICON_NAMES, ICON_SIZES } from './icon';
|
export { Icon, ICON_NAMES, ICON_SIZES } from './icon';
|
||||||
export { Label } from './label';
|
export { Label } from './label';
|
||||||
|
Loading…
x
Reference in New Issue
Block a user