Skip to content
Merged
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
28 changes: 28 additions & 0 deletions documentation-site/examples/tag-group/hierachy.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import * as React from "react";
import { styled } from "baseui";
import { Tag, SUPPORTED_KIND } from "baseui/tag";
import { TagGroup, HIERARCHY } from "baseui/tag-group";

const Container = styled("div", {
display: "flex",
flexDirection: "column",
gap: "32px",
});

export default function Example() {
return (
<Container>
{Object.values(HIERARCHY).map((hierarchy) => (
<TagGroup hierarchy={hierarchy} key={hierarchy}>
{Object.values(SUPPORTED_KIND)
.filter((kind) => kind !== SUPPORTED_KIND.custom)
.map((kind) => (
<Tag key={kind} kind={kind}>
{kind}
</Tag>
))}
</TagGroup>
))}
</Container>
);
}
33 changes: 33 additions & 0 deletions documentation-site/examples/tag-group/size.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import * as React from "react";
import { styled } from "baseui";
import { Tag } from "baseui/tag";
import { TagGroup, SIZE } from "baseui/tag-group";
import { LabelSmall } from "baseui/typography";

const Container = styled("div", {
display: "flex",
flexDirection: "column",
gap: "32px",
});

const Row = styled("div", {
display: "flex",
flexDirection: "column",
gap: "8px",
});

export default function Example() {
return (
<Container>
{Object.values(SIZE).map((size) => (
<Row key={size}>
<LabelSmall>{size}</LabelSmall>
<TagGroup size={size}>
<Tag>gray</Tag>
<Tag kind="red">red</Tag>
</TagGroup>
</Row>
))}
</Container>
);
}
115 changes: 115 additions & 0 deletions documentation-site/examples/tag-group/wrap.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import * as React from "react";
import { useStyletron } from "baseui";
import { Tag, SUPPORTED_KIND } from "baseui/tag";
import { TagGroup } from "baseui/tag-group";
import { HeadingSmall, ParagraphSmall } from "baseui/typography";

export default function Example() {
const [css] = useStyletron();
return (
<React.Fragment>
<HeadingSmall>Wrap: true (default)</HeadingSmall>

<ParagraphSmall>no width set on container</ParagraphSmall>
<TagGroup>
{Object.values(SUPPORTED_KIND)
.filter((kind) => kind !== SUPPORTED_KIND.custom)
.map((kind) => (
<Tag key={kind} kind={kind}>
{kind}
</Tag>
))}
</TagGroup>

<ParagraphSmall>width set to 300px on container </ParagraphSmall>
<div className={css({ width: "300px" })}>
<TagGroup>
{Object.values(SUPPORTED_KIND)
.filter((kind) => kind !== SUPPORTED_KIND.custom)
.map((kind) => (
<Tag key={kind} kind={kind}>
{kind}
</Tag>
))}
</TagGroup>
</div>

<ParagraphSmall>
width set to 300px on container and tag with long text{" "}
</ParagraphSmall>
<div className={css({ width: "300px" })}>
<TagGroup>
{Object.values(SUPPORTED_KIND)
.filter((kind) => kind !== SUPPORTED_KIND.custom)
.map((kind, index) => (
<Tag key={kind} kind={kind}>
{index === 1
? "A very long tag text to test wrapping behavior"
: kind}
</Tag>
))}
</TagGroup>
</div>

<ParagraphSmall>
width set to 100px(smaller than tag text default max width) on container
and tag with long text - very extreme case
</ParagraphSmall>
<div className={css({ marginBottom: "40px", width: "100px" })}>
<TagGroup>
{Object.values(SUPPORTED_KIND)
.filter((kind) => kind !== SUPPORTED_KIND.custom)
.map((kind, index) => (
<Tag key={kind} kind={kind}>
{index === 1
? "A very long tag text to test wrapping behavior"
: kind}
</Tag>
))}
</TagGroup>
</div>

<HeadingSmall>Wrap: false</HeadingSmall>
<ParagraphSmall>no width set on container</ParagraphSmall>
<TagGroup>
{Object.values(SUPPORTED_KIND)
.filter((kind) => kind !== SUPPORTED_KIND.custom)
.map((kind) => (
<Tag key={kind} kind={kind}>
{kind}
</Tag>
))}
</TagGroup>

<ParagraphSmall>width set to 300px on container </ParagraphSmall>
<div className={css({ width: "300px" })}>
<TagGroup wrap={false}>
{Object.values(SUPPORTED_KIND)
.filter((kind) => kind !== SUPPORTED_KIND.custom)
.map((kind) => (
<Tag key={kind} kind={kind}>
{kind}
</Tag>
))}
</TagGroup>
</div>

<ParagraphSmall>
width set to 300px on container and tag with long text{" "}
</ParagraphSmall>
<div className={css({ marginBottom: "40px", width: "300px" })}>
<TagGroup wrap={false}>
{Object.values(SUPPORTED_KIND)
.filter((kind) => kind !== SUPPORTED_KIND.custom)
.map((kind, index) => (
<Tag key={kind} kind={kind}>
{index === 1
? "A very long tag text to test wrapping behavior"
: kind}
</Tag>
))}
</TagGroup>
</div>
</React.Fragment>
);
}
36 changes: 36 additions & 0 deletions documentation-site/pages/components/tag-group.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import Example from "../../components/example";
import Layout from "../../components/layout";
import Exports from "../../components/exports";

import Hierarchy from "examples/tag-group/hierachy.tsx";
import Size from "examples/tag-group/size.tsx";
import Wrap from "examples/tag-group/wrap.tsx";

import { TagGroup } from "baseui/tag-group";
import * as TagGroupExports from "baseui/tag-group";

export default Layout;

# Tag Group

Groups multiple tags together with shared styling and behavior.

## Examples

<Example title="Tag Group hierarchy" path="tag-group/hierachy.tsx">
<Hierarchy />
</Example>

<Example title="Tag Group size" path="tag-group/size.tsx">
<Size />
</Example>

<Example title="Tag Group wrap" path="tag-group/wrap.tsx">
<Wrap />
</Example>

<Exports
component={TagGroupExports}
title="Tag Group exports"
path="baseui/tag-group"
/>
4 changes: 4 additions & 0 deletions documentation-site/routes.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,10 @@ const routes = [
title: 'Tag',
itemId: '/components/tag',
},
{
title: 'Tag Group',
itemId: '/components/tag-group',
},
{
title: 'Tile',
itemId: '/components/tile',
Expand Down
16 changes: 16 additions & 0 deletions src/tag-group/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
Copyright (c) Uber Technologies, Inc.

This source code is licensed under the MIT license found in the
LICENSE file in the root directory of this source tree.
*/

import { SIZE, HIERARCHY } from '../tag';

export { SIZE, HIERARCHY };

export * from './types';

export { default as TagGroup } from './tag-group';

export { StyledRoot } from './styled-components';
48 changes: 48 additions & 0 deletions src/tag-group/styled-components.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
Copyright (c) Uber Technologies, Inc.

This source code is licensed under the MIT license found in the
LICENSE file in the root directory of this source tree.
*/


import { styled, type Theme } from '../styles';
import type { Properties } from 'csstype';

export const StyledRoot = styled<
'div',
{
$wrap?: boolean;
}
>('div', ({ $wrap, $theme }) => {
return {
display: 'flex',
columnGap: $theme.sizing.scale300,
rowGap: $theme.sizing.scale300,
...getWrapStyles({ $wrap, $theme }),
};
});
StyledRoot.displayName = 'StyledRoot';

type WrapStyles = {
flexWrap?: Properties['flexWrap'];
overflowX?: Properties['overflowX'];
scrollbarWidth?: Properties['scrollbarWidth'];
padding?: Properties['padding'];
};

const getWrapStyles = ({ $wrap, $theme }): WrapStyles => {
if (typeof $wrap === 'boolean') {
return $wrap
? {
padding: 0,
flexWrap: 'wrap',
}
: {
overflowX: 'auto',
scrollbarWidth: 'none',
padding: `0 ${$theme.sizing.scale600}`,
};
}
return {};
};
66 changes: 66 additions & 0 deletions src/tag-group/tag-group.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
Copyright (c) Uber Technologies, Inc.

This source code is licensed under the MIT license found in the
LICENSE file in the root directory of this source tree.
*/

import * as React from 'react';

import { KIND, SIZE } from '../tag';
import { getOverrides } from '../helpers/overrides';

import { StyledRoot } from './styled-components';
import type { TagGroupProps } from './types';

const TagGroup = React.forwardRef<HTMLDivElement, TagGroupProps>((props, ref) => {
const {
children,
wrap = true,
hierarchy = KIND.primary,
size = SIZE.small,
overrides = {},
} = props;

const [Root, rootProps] = getOverrides(overrides.Root, StyledRoot);
const styleProps = {
$wrap: wrap,
};

return (
<Root ref={ref} {...styleProps} {...rootProps}>
{React.Children.map(children, (child) => {
if (!React.isValidElement(child)) {
return null;
}

return React.cloneElement(child, {
hierarchy,

Check failure on line 38 in src/tag-group/tag-group.tsx

View workflow job for this annotation

GitHub Actions / Build and Type Check

No overload matches this call.
size,
// All tags in tag group are display only
onActionClick: undefined,
onActionKeyDown: undefined,
onClick: undefined,
onKeyDown: undefined,
closeable: false,
overrides: {
Root: {
style: {
// Single Tag has default margin, reset it to 0 in TagGroup
margin: 0,
...(wrap ? { maxWidth: '100%' } : {}), // ensure wrapping works correctly even if Tag itself has a custom maxWidth(Tag has a default maxWidth 128px on Text inside)
...child.props.overrides?.Root?.style,
},
...child.props.overrides?.Root,
},
...child.props.overrides,
},
});
})}
</Root>
);
});

TagGroup.displayName = 'TagGroup';

export default TagGroup;
19 changes: 19 additions & 0 deletions src/tag-group/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type * as React from 'react';
import type { Override } from '../helpers/overrides';
import type { HIERARCHY, SIZE } from '../tag';

export type TagGroupProps = {
/** Set of more than one `Button` components */
children: Array<React.ReactNode>;
overrides?: TagGroupOverrides;
/** Determines whether tags should wrap to the next line when they exceed the container width. Defaults to true */
wrap?: boolean;
/** Defines tags look in the tag group. Set it to one of HIERARCHY[key] values. Defaults to HIERARCHY.primary */
hierarchy?: (typeof HIERARCHY)[keyof typeof HIERARCHY];
/** Determines the size of the Tag in the tag group. Defaults to small */
size?: (typeof SIZE)[keyof typeof SIZE];
};

type TagGroupOverrides = {
Root?: Override;
};
Loading