From 0f57b5d23d4be1c36dde6e765396711aed6ef9b7 Mon Sep 17 00:00:00 2001 From: Leonardo Giacone Date: Mon, 2 Dec 2024 10:10:29 +0100 Subject: [PATCH 1/4] feat: new Tag component --- packages/admin-ui/src/Tag/Tag.stories.tsx | 142 ++++++++++++++++++++++ packages/admin-ui/src/Tag/Tag.tsx | 90 ++++++++++++++ packages/admin-ui/src/Tag/index.ts | 1 + packages/admin-ui/src/index.ts | 1 + 4 files changed, 234 insertions(+) create mode 100644 packages/admin-ui/src/Tag/Tag.stories.tsx create mode 100644 packages/admin-ui/src/Tag/Tag.tsx create mode 100644 packages/admin-ui/src/Tag/index.ts diff --git a/packages/admin-ui/src/Tag/Tag.stories.tsx b/packages/admin-ui/src/Tag/Tag.stories.tsx new file mode 100644 index 0000000000..9b011c3873 --- /dev/null +++ b/packages/admin-ui/src/Tag/Tag.stories.tsx @@ -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 = { + title: "Components/Tag", + component: Tag, + tags: ["autodocs"], + parameters: { + layout: "padded" + } +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + text: "Label" + } +}; + +export const WithIcon: Story = { + args: { + text: "Label", + icon: } 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 + } +}; diff --git a/packages/admin-ui/src/Tag/Tag.tsx b/packages/admin-ui/src/Tag/Tag.tsx new file mode 100644 index 0000000000..c36d8a0dfd --- /dev/null +++ b/packages/admin-ui/src/Tag/Tag.tsx @@ -0,0 +1,90 @@ +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", + "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, "children">, + VariantProps { + text: string; + icon: React.ReactElement; + disabled?: boolean; +} + +const DecoratableTag = ({ className, variant, text, icon, disabled, ...props }: TagProps) => { + const hasIcon = React.useMemo(() => Boolean(icon), [icon]); + + return ( + + {text} {icon} + + ); +}; +const Tag = makeDecoratable("Tag", DecoratableTag); + +export { Tag }; diff --git a/packages/admin-ui/src/Tag/index.ts b/packages/admin-ui/src/Tag/index.ts new file mode 100644 index 0000000000..5b4c58bceb --- /dev/null +++ b/packages/admin-ui/src/Tag/index.ts @@ -0,0 +1 @@ +export * from "./Tag"; diff --git a/packages/admin-ui/src/index.ts b/packages/admin-ui/src/index.ts index 3c1dc27593..021c9b3c3b 100644 --- a/packages/admin-ui/src/index.ts +++ b/packages/admin-ui/src/index.ts @@ -16,6 +16,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"; From 1fbe55842d0de5285c2b4a37251233e01f09908f Mon Sep 17 00:00:00 2001 From: Leonardo Giacone Date: Mon, 2 Dec 2024 10:19:00 +0100 Subject: [PATCH 2/4] chore: change text to label --- packages/admin-ui/src/Tag/Tag.stories.tsx | 4 ++-- packages/admin-ui/src/Tag/Tag.tsx | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/admin-ui/src/Tag/Tag.stories.tsx b/packages/admin-ui/src/Tag/Tag.stories.tsx index 9b011c3873..15beaeda68 100644 --- a/packages/admin-ui/src/Tag/Tag.stories.tsx +++ b/packages/admin-ui/src/Tag/Tag.stories.tsx @@ -18,13 +18,13 @@ type Story = StoryObj; export const Default: Story = { args: { - text: "Label" + label: "Label" } }; export const WithIcon: Story = { args: { - text: "Label", + ...Default.args, icon: } label={"Close"} size={"sm"} /> } }; diff --git a/packages/admin-ui/src/Tag/Tag.tsx b/packages/admin-ui/src/Tag/Tag.tsx index c36d8a0dfd..21f00261cc 100644 --- a/packages/admin-ui/src/Tag/Tag.tsx +++ b/packages/admin-ui/src/Tag/Tag.tsx @@ -67,12 +67,12 @@ const tagVariants = cva( export interface TagProps extends Omit, "children">, VariantProps { - text: string; + label: string; icon: React.ReactElement; disabled?: boolean; } -const DecoratableTag = ({ className, variant, text, icon, disabled, ...props }: TagProps) => { +const DecoratableTag = ({ className, variant, label, icon, disabled, ...props }: TagProps) => { const hasIcon = React.useMemo(() => Boolean(icon), [icon]); return ( @@ -81,7 +81,7 @@ const DecoratableTag = ({ className, variant, text, icon, disabled, ...props }: className={cn(tagVariants({ variant, hasIcon }), className)} aria-disabled={disabled} > - {text} {icon} + {label} {icon} ); }; From 62fb9c7d1cd9b361aff9d70d66f4c5b4e543ca63 Mon Sep 17 00:00:00 2001 From: Leonardo Giacone Date: Mon, 2 Dec 2024 11:39:15 +0100 Subject: [PATCH 3/4] refactor: deprecate RMWC chip --- packages/admin-ui/src/Tag/Tag.stories.tsx | 2 +- packages/admin-ui/src/Tag/Tag.tsx | 7 +-- packages/ui/package.json | 1 - packages/ui/src/Chips/Chip.tsx | 47 ++++++++++++++----- packages/ui/src/Chips/Chips.tsx | 17 +++---- packages/ui/src/Chips/README.md | 15 ------ .../ui/src/Chips/icons/baseline-done-24px.svg | 4 -- .../src/Chips/icons/baseline-email-24px.svg | 3 -- packages/ui/src/Chips/styles.ts | 19 -------- yarn.lock | 1 - 10 files changed, 47 insertions(+), 69 deletions(-) delete mode 100644 packages/ui/src/Chips/README.md delete mode 100644 packages/ui/src/Chips/icons/baseline-done-24px.svg delete mode 100644 packages/ui/src/Chips/icons/baseline-email-24px.svg delete mode 100644 packages/ui/src/Chips/styles.ts diff --git a/packages/admin-ui/src/Tag/Tag.stories.tsx b/packages/admin-ui/src/Tag/Tag.stories.tsx index 15beaeda68..1053fd4337 100644 --- a/packages/admin-ui/src/Tag/Tag.stories.tsx +++ b/packages/admin-ui/src/Tag/Tag.stories.tsx @@ -2,7 +2,7 @@ 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"; +import { Icon } from "~/Icon"; const meta: Meta = { title: "Components/Tag", diff --git a/packages/admin-ui/src/Tag/Tag.tsx b/packages/admin-ui/src/Tag/Tag.tsx index 21f00261cc..3b9e17ec26 100644 --- a/packages/admin-ui/src/Tag/Tag.tsx +++ b/packages/admin-ui/src/Tag/Tag.tsx @@ -67,8 +67,8 @@ const tagVariants = cva( export interface TagProps extends Omit, "children">, VariantProps { - label: string; - icon: React.ReactElement; + label: React.ReactNode; + icon: React.ReactElement | React.ReactNode; disabled?: boolean; } @@ -81,7 +81,8 @@ const DecoratableTag = ({ className, variant, label, icon, disabled, ...props }: className={cn(tagVariants({ variant, hasIcon }), className)} aria-disabled={disabled} > - {label} {icon} + {label} + {icon} ); }; diff --git a/packages/ui/package.json b/packages/ui/package.json index 449ae443e2..0b826d23d7 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -19,7 +19,6 @@ "@emotion/styled": "^11.10.6", "@material-design-icons/svg": "^0.14.3", "@rmwc/checkbox": "^14.2.2", - "@rmwc/chip": "^14.2.2", "@rmwc/data-table": "^14.2.2", "@rmwc/dialog": "^14.2.2", "@rmwc/drawer": "^14.2.2", diff --git a/packages/ui/src/Chips/Chip.tsx b/packages/ui/src/Chips/Chip.tsx index 6919813845..1865e770a7 100644 --- a/packages/ui/src/Chips/Chip.tsx +++ b/packages/ui/src/Chips/Chip.tsx @@ -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 { onRemove: (ev: React.MouseEvent) => 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) => { @@ -21,16 +52,8 @@ export const Chip = (props: ChipProps) => { let trailingIconElement = null; if (trailingIcon) { - trailingIconElement = ( - - {trailingIcon} - - ); + trailingIconElement = {trailingIcon}; } - return ( - - {children} {trailingIconElement} - - ); + return ; }; diff --git a/packages/ui/src/Chips/Chips.tsx b/packages/ui/src/Chips/Chips.tsx index 2069c250cd..723c7ae84e 100644 --- a/packages/ui/src/Chips/Chips.tsx +++ b/packages/ui/src/Chips/Chips.tsx @@ -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 { /** @@ -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 ( - +
{children} - +
); }; diff --git a/packages/ui/src/Chips/README.md b/packages/ui/src/Chips/README.md deleted file mode 100644 index b8b18c4809..0000000000 --- a/packages/ui/src/Chips/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# Chips - -### Design - -https://material.io/design/components/chips.html - -### Description - -`Chips` allow users to enter information, make selections, filter content, or trigger actions. - -### Import - -```js -import { Chips, Chip, ChipText, ChipIcon } from "@webiny/ui/Chips"; -``` diff --git a/packages/ui/src/Chips/icons/baseline-done-24px.svg b/packages/ui/src/Chips/icons/baseline-done-24px.svg deleted file mode 100644 index 0482857b7b..0000000000 --- a/packages/ui/src/Chips/icons/baseline-done-24px.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/packages/ui/src/Chips/icons/baseline-email-24px.svg b/packages/ui/src/Chips/icons/baseline-email-24px.svg deleted file mode 100644 index b7fb834940..0000000000 --- a/packages/ui/src/Chips/icons/baseline-email-24px.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/packages/ui/src/Chips/styles.ts b/packages/ui/src/Chips/styles.ts deleted file mode 100644 index 6306cba64c..0000000000 --- a/packages/ui/src/Chips/styles.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { css } from "emotion"; - -export const chipIconWrapper = css({ - ".mdc-evolution-chip__text-label": { - display: "flex", - alignItems: "center", - ".mdc-chip__icon": { - svg: { - width: 18, - height: 18 - } - } - } -}); - -export const disabledChips = css({ - opacity: 0.75, - pointerEvents: "none" -}); diff --git a/yarn.lock b/yarn.lock index 4752e885fb..69fbe7f2ab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18994,7 +18994,6 @@ __metadata: "@emotion/styled": ^11.10.6 "@material-design-icons/svg": ^0.14.3 "@rmwc/checkbox": ^14.2.2 - "@rmwc/chip": ^14.2.2 "@rmwc/data-table": ^14.2.2 "@rmwc/dialog": ^14.2.2 "@rmwc/drawer": ^14.2.2 From 2fc9fcd868dbf6fca5dcf73735e450c669f803e4 Mon Sep 17 00:00:00 2001 From: Leonardo Giacone Date: Wed, 11 Dec 2024 14:40:39 +0100 Subject: [PATCH 4/4] feat: add text truncation for overflow handling --- packages/admin-ui/src/Tag/Tag.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/admin-ui/src/Tag/Tag.tsx b/packages/admin-ui/src/Tag/Tag.tsx index 3b9e17ec26..3374df97bf 100644 --- a/packages/admin-ui/src/Tag/Tag.tsx +++ b/packages/admin-ui/src/Tag/Tag.tsx @@ -4,7 +4,7 @@ import { Icon } from "~/Icon"; const tagVariants = cva( [ - "inline-flex items-center gap-xxs rounded-sm text-sm text-regular transition-colors cursor-default", + "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" ], @@ -81,7 +81,7 @@ const DecoratableTag = ({ className, variant, label, icon, disabled, ...props }: className={cn(tagVariants({ variant, hasIcon }), className)} aria-disabled={disabled} > - {label} + {label} {icon} );