Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(admin-ui): Tag component #4423

Open
wants to merge 5 commits into
base: feat/new-admin-ui
Choose a base branch
from
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
142 changes: 142 additions & 0 deletions packages/admin-ui/src/Tag/Tag.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import React from "react";
import { ReactComponent as XIcon } from "@material-design-icons/svg/round/close.svg";
import type { Meta, StoryObj } from "@storybook/react";
import { Tag } from "./Tag";
import { Icon } from "~/Icon";

const meta: Meta<typeof Tag> = {
title: "Components/Tag",
component: Tag,
tags: ["autodocs"],
parameters: {
layout: "padded"
}
};

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

export const Default: Story = {
args: {
label: "Label"
}
};

export const WithIcon: Story = {
args: {
...Default.args,
icon: <Icon icon={<XIcon />} label={"Close"} size={"sm"} />
}
};

export const NeutralBase: Story = {
args: {
...WithIcon.args,
variant: "neutral-base"
}
};

export const NeutralBaseDisabled: Story = {
args: {
...NeutralBase.args,
disabled: true
}
};

export const NeutralLight: Story = {
args: {
...WithIcon.args,
variant: "neutral-light"
}
};

export const NeutralLightDisabled: Story = {
args: {
...NeutralLight.args,
disabled: true
}
};

export const NeutralStrong: Story = {
args: {
...WithIcon.args,
variant: "neutral-strong"
}
};

export const NeutralStrongDisabled: Story = {
args: {
...NeutralStrong.args,
disabled: true
}
};

export const NeutralDark: Story = {
args: {
...WithIcon.args,
variant: "neutral-dark"
}
};

export const NeutralDarkDisabled: Story = {
args: {
...NeutralDark.args,
disabled: true
}
};

export const Accent: Story = {
args: {
...WithIcon.args,
variant: "accent"
}
};

export const AccentDisabled: Story = {
args: {
...Accent.args,
disabled: true
}
};

export const Success: Story = {
args: {
...WithIcon.args,
variant: "success"
}
};

export const SuccessDisabled: Story = {
args: {
...Success.args,
disabled: true
}
};

export const Warning: Story = {
args: {
...WithIcon.args,
variant: "warning"
}
};

export const WarningDisabled: Story = {
args: {
...Warning.args,
disabled: true
}
};

export const Destructive: Story = {
args: {
...WithIcon.args,
variant: "destructive"
}
};

export const DestructiveDisabled: Story = {
args: {
...Destructive.args,
disabled: true
}
};
91 changes: 91 additions & 0 deletions packages/admin-ui/src/Tag/Tag.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import * as React from "react";
import { cn, cva, makeDecoratable, type VariantProps } from "~/utils";
import { Icon } from "~/Icon";

const tagVariants = cva(
[
"inline-flex items-center gap-xxs rounded-sm text-sm text-regular transition-colors cursor-default overflow-hidden",
"focus:outline-none",
"aria-disabled:cursor-not-allowed"
],
{
variants: {
hasIcon: {
true: "pl-xs-plus pt-xxs pb-xxs pr-xxs",
false: "pl-xs-plus pr-xs-plus pt-xxs pb-xxs"
},
variant: {
"neutral-base": [
"border-sm px-[calc(theme(padding.xs-plus)-theme(borderWidth.sm))] py-[calc(theme(padding.xxs)-theme(borderWidth.sm))]",
"bg-transparent border-neutral-muted text-neutral-primary fill-neutral-xstrong/50",
"hover:bg-neutral-light",
"aria-disabled:bg-transparent aria-disabled:border-neutral-dimmed aria-disabled:text-neutral-disabled aria-disabled:fill-neutral-xstrong/25"
],
"neutral-light": [
"bg-neutral-muted text-neutral-primary fill-neutral-xstrong/50",
"hover:bg-neutral-strong",
"aria-disabled:bg-neutral-muted aria-disabled:text-neutral-muted aria-disabled:fill-neutral-xstrong/25"
],
"neutral-strong": [
"bg-neutral-xstrong text-neutral-light fill-neutral-base/60",
"hover:bg-neutral-dark",
"aria-disabled:bg-neutral-strong aria-disabled:fill-neutral-base/50"
],
"neutral-dark": [
"bg-neutral-dark text-neutral-light fill-neutral-base/60",
"hover:bg-neutral-xstrong",
"aria-disabled:bg-neutral-strong aria-disabled:fill-neutral-base/50"
],
accent: [
"bg-primary-default text-neutral-light fill-neutral-base/60",
"hover:bg-primary-strong",
"aria-disabled:bg-primary-disabled aria-disabled:fill-neutral-base/50"
],
success: [
"bg-success-default text-neutral-light fill-neutral-base/60",
"hover:bg-success-strong",
"aria-disabled:bg-success-disabled aria-disabled:fill-neutral-base/50"
],
warning: [
"bg-warning-muted text-neutral-primary fill-neutral-xstrong/50",
"hover:bg-warning-default",
"aria-disabled:bg-warning-disabled aria-disabled:text-neutral-disabled aria-disabled:fill-neutral-xstrong/25"
],
destructive: [
"bg-destructive-default text-neutral-light fill-neutral-base fill-neutral-base/60",
"hover:bg-destructive-strong",
"aria-disabled:bg-destructive-disabled aria-disabled:fill-neutral-base/50"
]
}
},
defaultVariants: {
variant: "neutral-base"
}
}
);

export interface TagProps
extends Omit<React.HTMLAttributes<HTMLSpanElement>, "children">,
VariantProps<typeof tagVariants> {
label: React.ReactNode;
icon: React.ReactElement<typeof Icon> | React.ReactNode;
disabled?: boolean;
}

const DecoratableTag = ({ className, variant, label, icon, disabled, ...props }: TagProps) => {
const hasIcon = React.useMemo(() => Boolean(icon), [icon]);

return (
<span
{...props}
className={cn(tagVariants({ variant, hasIcon }), className)}
aria-disabled={disabled}
>
<span className={"overflow-hidden truncate whitespace-nowrap"}>{label}</span>
{icon}
</span>
);
};
const Tag = makeDecoratable("Tag", DecoratableTag);

export { Tag };
1 change: 1 addition & 0 deletions packages/admin-ui/src/Tag/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./Tag";
1 change: 1 addition & 0 deletions packages/admin-ui/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export * from "./Separator";
export * from "./Slider";
export * from "./Switch";
export * from "./Tabs";
export * from "./Tag";
export * from "./Text";
export * from "./Textarea";
export * from "./Toast";
Expand Down
1 change: 0 additions & 1 deletion packages/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
"@emotion/react": "^11.10.6",
"@emotion/styled": "^11.10.6",
"@material-design-icons/svg": "^0.14.3",
"@rmwc/chip": "^14.2.2",
"@rmwc/data-table": "^14.2.2",
"@rmwc/dialog": "^14.2.2",
"@rmwc/drawer": "^14.2.2",
Expand Down
47 changes: 35 additions & 12 deletions packages/ui/src/Chips/Chip.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,44 @@
import React, { useCallback } from "react";
import { Chip as RmwcChip, ChipProps as RmwcChipProps } from "@rmwc/chip";
import { Tag as AdminTag } from "@webiny/admin-ui";

export interface RmwcChipProps {
/** Text for your Chip. */
label?: React.ReactNode;
/** makes the Chip appear selected. */
selected?: boolean;
/** Instance of an Icon Component. */
icon?: any;
/** Instance of an Icon Component. */
trailingIcon?: any;
/** Defaults to true. Set this to false if your trailing icon is something other than a remove button. */
trailingIconRemovesChip?: boolean;
/** An optional chip ID that will be included in callback evt.detail. If this is not passed, RMWC will attempt to use the "key" prop if present. */
id?: string;
/** Includes an optional checkmark for the chips selected state. */
checkmark?: boolean;
/** Additional children will be rendered in the text area. */
children?: React.ReactNode;
/** A callback for click or enter key. This should be used over onClick for accessibility reasons. evt.detail = { chipId: string } */
onInteraction?: (evt: any) => void;
/** A callback for click or enter key for the trailing icon. material-components-web always treats this as an intent to remove the chip. evt.detail = { chipId: string } */
onTrailingIconInteraction?: (evt: any) => void;
/** A callback that is fired once the chip is in an exited state from removing it. evt.detail = { chipId: string } */
onRemove?: (evt: any) => void;
/** Advanced: A reference to the MDCFoundation. */
foundationRef?: any;
}

export interface ChipProps extends Omit<RmwcChipProps, "onRemove" | "trailingIcon"> {
onRemove: (ev: React.MouseEvent<HTMLSpanElement>) => void;
trailingIcon?: React.ReactNode;
}

/**
* @deprecated This component is deprecated and will be removed in future releases.
* Please use the `Tag` component from the `@webiny/admin-ui` package instead.
*/
export const Chip = (props: ChipProps) => {
const { children, trailingIcon, onRemove, ...rest } = props;
const { children, label, trailingIcon, onRemove, ...rest } = props;

const onRemoveCb = useCallback(
(ev: React.MouseEvent<HTMLSpanElement>) => {
Expand All @@ -21,16 +52,8 @@ export const Chip = (props: ChipProps) => {

let trailingIconElement = null;
if (trailingIcon) {
trailingIconElement = (
<span className={"mdc-chip__icon mdc-chip__icon--trailing"} onClick={onRemoveCb}>
{trailingIcon}
</span>
);
trailingIconElement = <span onClick={onRemoveCb}>{trailingIcon}</span>;
}

return (
<RmwcChip {...rest}>
{children} {trailingIconElement}
</RmwcChip>
);
return <AdminTag {...rest} label={children || label} icon={trailingIconElement} />;
};
17 changes: 7 additions & 10 deletions packages/ui/src/Chips/Chips.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import React from "react";
import classNames from "classnames";
import { ChipSet } from "@rmwc/chip";
import { Chip } from "./Chip";
import { chipIconWrapper, disabledChips } from "./styles";

export interface ChipsProps {
/**
Expand All @@ -26,17 +24,16 @@ export interface ChipsProps {
style?: React.CSSProperties;
}

/**
* @deprecated This component is deprecated and will be removed in future releases.
* Please use the `Tag` component from the `@webiny/admin-ui` package instead.
*/
export const Chips = (props: ChipsProps) => {
const { children, className, disabled, ...rest } = props;
const { children, className, ...rest } = props;

return (
<ChipSet
{...rest}
className={classNames("mdc-evolution-chip-set", className, chipIconWrapper, {
[disabledChips]: disabled
})}
>
<div {...rest} className={classNames("mdc-evolution-chip-set", className)}>
{children}
</ChipSet>
</div>
);
};
15 changes: 0 additions & 15 deletions packages/ui/src/Chips/README.md

This file was deleted.

4 changes: 0 additions & 4 deletions packages/ui/src/Chips/icons/baseline-done-24px.svg

This file was deleted.

3 changes: 0 additions & 3 deletions packages/ui/src/Chips/icons/baseline-email-24px.svg

This file was deleted.

19 changes: 0 additions & 19 deletions packages/ui/src/Chips/styles.ts

This file was deleted.

Loading
Loading