feat: editable dismissible tab component (#38788)
## Description Addition of a template component that combines editable entity name and dismissible tab. Fixes #37649 ## Automation /ok-to-test tags="@tag.Sanity" ### 🔍 Cypress test results <!-- This is an auto-generated comment: Cypress test results --> > [!CAUTION] > 🔴 🔴 🔴 Some tests have failed. > Workflow run: <https://github.com/appsmithorg/appsmith/actions/runs/12925482595> > Commit: 23c8fbe877a2390ec95877ae8f761d7c590b23c7 > <a href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=12925482595&attempt=2&selectiontype=test&testsstatus=failed&specsstatus=fail" target="_blank">Cypress dashboard</a>. > Tags: @tag.Sanity > Spec: > The following are new failures, please fix them before merging the PR: <ol> > <li>cypress/e2e/Regression/ClientSide/OtherUIFeatures/GlobalSearch_spec.js</ol> > <a href="https://internal.appsmith.com/app/cypress-dashboard/identified-flaky-tests-65890b3c81d7400d08fa9ee3?branch=master" target="_blank">List of identified flaky tests</a>. > <hr>Thu, 23 Jan 2025 11:42:24 UTC <!-- end of auto-generated comment: Cypress test results --> ## Communication Should the DevRel and Marketing teams inform users about this change? - [ ] Yes - [ ] No <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced `DismissibleTab` component with interactive close and click functionality. - Added `EditableEntityName` component for editing entity names with validation. - Created `EditableDismissibleTab` component combining dismissible and editable behaviors. - Added new Storybook stories for `DismissibleTab`, `EditableDismissibleTab`, and `EditableEntityName` components. - **Improvements** - Enhanced design system with new styled components for better interactivity and appearance. - **Refactoring** - Reorganized hook and component imports. - Updated export statements in various files to improve module accessibility. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
parent
72251cba2e
commit
efceb1e390
|
|
@ -0,0 +1,21 @@
|
||||||
|
/* eslint-disable no-console */
|
||||||
|
import type { Meta, StoryObj } from "@storybook/react";
|
||||||
|
|
||||||
|
import { DismissibleTab } from ".";
|
||||||
|
|
||||||
|
const meta: Meta<typeof DismissibleTab> = {
|
||||||
|
title: "ADS/Components/Dismissible Tab",
|
||||||
|
component: DismissibleTab,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
|
||||||
|
type Story = StoryObj<typeof DismissibleTab>;
|
||||||
|
|
||||||
|
export const Basic: Story = {
|
||||||
|
args: {
|
||||||
|
isActive: true,
|
||||||
|
dataTestId: "t--dismissible-tab",
|
||||||
|
children: "Dismissible tab",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
import styled from "styled-components";
|
||||||
|
|
||||||
|
import { Button as ADSButton } from "..";
|
||||||
|
|
||||||
|
export const Tab = styled.div`
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
gap: var(--ads-v2-spaces-2);
|
||||||
|
height: 100%;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--ads-v2-color-fg);
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
border-top-left-radius: var(--ads-v2-border-radius);
|
||||||
|
border-top-right-radius: var(--ads-v2-border-radius);
|
||||||
|
border-left: 1px solid transparent;
|
||||||
|
border-right: 1px solid transparent;
|
||||||
|
border-top: 3px solid transparent;
|
||||||
|
|
||||||
|
padding: var(--ads-v2-spaces-3);
|
||||||
|
padding-top: 6px;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background: var(--ads-v2-colors-control-field-default-bg);
|
||||||
|
border-top-color: var(--ads-v2-color-bg-brand);
|
||||||
|
border-left-color: var(--ads-v2-color-border-muted);
|
||||||
|
border-right-color: var(--ads-v2-color-border-muted);
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-weight: var(--ads-v2-font-weight-bold);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .tab-close {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover > .tab-close,
|
||||||
|
&:focus-within > .tab-close,
|
||||||
|
&.active > .tab-close {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const CloseButton = styled(ADSButton)`
|
||||||
|
border-radius: 2px;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: var(--ads-v2-spaces-1);
|
||||||
|
max-width: 16px;
|
||||||
|
max-height: 16px;
|
||||||
|
`;
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import clsx from "classnames";
|
||||||
|
|
||||||
|
import { Icon } from "..";
|
||||||
|
|
||||||
|
import * as Styled from "./DismissibleTab.styles";
|
||||||
|
import { DATA_TEST_ID } from "./constants";
|
||||||
|
|
||||||
|
import type { DismissibleTabProps } from "./DismissibleTab.types";
|
||||||
|
|
||||||
|
export const DismissibleTab = ({
|
||||||
|
children,
|
||||||
|
dataTestId,
|
||||||
|
isActive,
|
||||||
|
onClick,
|
||||||
|
onClose,
|
||||||
|
onDoubleClick,
|
||||||
|
}: DismissibleTabProps) => {
|
||||||
|
return (
|
||||||
|
<Styled.Tab
|
||||||
|
className={clsx("editor-tab", isActive && "active")}
|
||||||
|
data-testid={dataTestId}
|
||||||
|
onClick={onClick}
|
||||||
|
onDoubleClick={onDoubleClick}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
<Styled.CloseButton
|
||||||
|
aria-label="Close tab"
|
||||||
|
className="tab-close"
|
||||||
|
data-testid={DATA_TEST_ID.CLOSE_BUTTON}
|
||||||
|
isIconButton
|
||||||
|
kind="tertiary"
|
||||||
|
onClick={onClose}
|
||||||
|
role="tab"
|
||||||
|
size="sm"
|
||||||
|
tabIndex={0}
|
||||||
|
>
|
||||||
|
<Icon name="close-line" />
|
||||||
|
</Styled.CloseButton>
|
||||||
|
</Styled.Tab>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
import type React from "react";
|
||||||
|
|
||||||
|
export interface DismissibleTabProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
dataTestId?: string;
|
||||||
|
isActive: boolean;
|
||||||
|
onClick: () => void;
|
||||||
|
onClose: (e: React.MouseEvent) => void;
|
||||||
|
onDoubleClick?: () => void;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
export const DATA_TEST_ID = {
|
||||||
|
CLOSE_BUTTON: "t--tab-close-btn",
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
export { DismissibleTab } from "./DismissibleTab";
|
||||||
|
export type { DismissibleTabProps } from "./DismissibleTab.types";
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
/* eslint-disable no-console */
|
||||||
|
import React from "react";
|
||||||
|
import type { Meta, StoryObj } from "@storybook/react";
|
||||||
|
|
||||||
|
import { EditableDismissibleTab } from ".";
|
||||||
|
import styled from "styled-components";
|
||||||
|
|
||||||
|
import { Icon } from "../..";
|
||||||
|
|
||||||
|
const meta: Meta<typeof EditableDismissibleTab> = {
|
||||||
|
title: "ADS/Templates/Editable Dismissible Tab",
|
||||||
|
component: EditableDismissibleTab,
|
||||||
|
};
|
||||||
|
|
||||||
|
const EntityIcon = styled.div`
|
||||||
|
height: 18px;
|
||||||
|
width: 18px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
svg,
|
||||||
|
img {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const JSIcon = () => {
|
||||||
|
return (
|
||||||
|
<EntityIcon>
|
||||||
|
<Icon name="js-yellow" size="md" />
|
||||||
|
</EntityIcon>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
|
||||||
|
type Story = StoryObj<typeof EditableDismissibleTab>;
|
||||||
|
|
||||||
|
export const Basic: Story = {
|
||||||
|
args: {
|
||||||
|
isActive: true,
|
||||||
|
dataTestId: "t--dismissible-tab",
|
||||||
|
icon: JSIcon(),
|
||||||
|
name: "Hello",
|
||||||
|
|
||||||
|
onNameSave: console.log,
|
||||||
|
validateName: (name: string) =>
|
||||||
|
name.length < 3 ? "Name must be at least 3 characters" : null,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
import React from "react";
|
||||||
|
import { noop } from "lodash";
|
||||||
|
import { useBoolean } from "usehooks-ts";
|
||||||
|
|
||||||
|
import { DismissibleTab } from "../..";
|
||||||
|
import { EditableEntityName } from "..";
|
||||||
|
|
||||||
|
import type { EditableDismissibleTabProps } from "./EditableDismissibleTab.types";
|
||||||
|
|
||||||
|
export const EditableDismissibleTab = (props: EditableDismissibleTabProps) => {
|
||||||
|
const {
|
||||||
|
dataTestId,
|
||||||
|
icon,
|
||||||
|
isActive,
|
||||||
|
isEditable = true,
|
||||||
|
isLoading,
|
||||||
|
name,
|
||||||
|
onClick,
|
||||||
|
onClose,
|
||||||
|
onNameSave,
|
||||||
|
validateName,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const {
|
||||||
|
setFalse: exitEditMode,
|
||||||
|
setTrue: enterEditMode,
|
||||||
|
value: isEditing,
|
||||||
|
} = useBoolean(false);
|
||||||
|
|
||||||
|
const handleDoubleClick = isEditable ? enterEditMode : noop;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DismissibleTab
|
||||||
|
dataTestId={dataTestId}
|
||||||
|
isActive={isActive}
|
||||||
|
onClick={onClick}
|
||||||
|
onClose={onClose}
|
||||||
|
onDoubleClick={handleDoubleClick}
|
||||||
|
>
|
||||||
|
<EditableEntityName
|
||||||
|
icon={icon}
|
||||||
|
isEditing={isEditing}
|
||||||
|
isLoading={isLoading}
|
||||||
|
name={name}
|
||||||
|
onExitEditing={exitEditMode}
|
||||||
|
onNameSave={onNameSave}
|
||||||
|
validateName={validateName}
|
||||||
|
/>
|
||||||
|
</DismissibleTab>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
import type React from "react";
|
||||||
|
|
||||||
|
export interface EditableDismissibleTabProps {
|
||||||
|
dataTestId?: string;
|
||||||
|
icon: React.ReactNode;
|
||||||
|
isActive: boolean;
|
||||||
|
isEditable?: boolean;
|
||||||
|
isLoading: boolean;
|
||||||
|
name: string;
|
||||||
|
onClick: () => void;
|
||||||
|
onClose: () => void;
|
||||||
|
onNameSave: (name: string) => void;
|
||||||
|
validateName: (name: string) => string | null;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
export { EditableDismissibleTab } from "./EditableDismissibleTab";
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
/* eslint-disable no-console */
|
||||||
|
import React from "react";
|
||||||
|
import type { Meta, StoryObj } from "@storybook/react";
|
||||||
|
import styled from "styled-components";
|
||||||
|
|
||||||
|
import { Icon } from "../..";
|
||||||
|
import { EditableEntityName } from ".";
|
||||||
|
|
||||||
|
const EntityIcon = styled.div`
|
||||||
|
height: 18px;
|
||||||
|
width: 18px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
svg,
|
||||||
|
img {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const JSIcon = () => {
|
||||||
|
return (
|
||||||
|
<EntityIcon>
|
||||||
|
<Icon name="js-yellow" size="md" />
|
||||||
|
</EntityIcon>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const meta: Meta<typeof EditableEntityName> = {
|
||||||
|
title: "ADS/Templates/Editable Entity Name",
|
||||||
|
component: EditableEntityName,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
|
||||||
|
type Story = StoryObj<typeof EditableEntityName>;
|
||||||
|
|
||||||
|
export const Basic: Story = {
|
||||||
|
args: {
|
||||||
|
name: "Hello",
|
||||||
|
onNameSave: console.log,
|
||||||
|
onExitEditing: console.log,
|
||||||
|
icon: JSIcon(),
|
||||||
|
inputTestId: "t--editable-name",
|
||||||
|
isEditing: true,
|
||||||
|
isLoading: false,
|
||||||
|
validateName: (name: string) =>
|
||||||
|
name.length < 3 ? "Name must be at least 3 characters" : null,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
import styled from "styled-components";
|
||||||
|
import { Text as ADSText } from "../../Text";
|
||||||
|
|
||||||
|
export const Root = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
justify-content: start;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--ads-v2-spaces-2);
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const Text = styled(ADSText)`
|
||||||
|
min-width: 3ch;
|
||||||
|
bottom: -0.5px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const IconContainer = styled.div`
|
||||||
|
height: 12px;
|
||||||
|
width: 12px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 12px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
import React, { useMemo } from "react";
|
||||||
|
|
||||||
|
import { Spinner, Tooltip } from "../..";
|
||||||
|
import { useEditableText } from "../../__hooks__";
|
||||||
|
|
||||||
|
import * as Styled from "./EditableEntityName.styles";
|
||||||
|
|
||||||
|
import type { EditableEntityNameProps } from "./EditableEntityName.types";
|
||||||
|
|
||||||
|
export const EditableEntityName = ({
|
||||||
|
icon,
|
||||||
|
inputTestId,
|
||||||
|
isEditing,
|
||||||
|
isLoading = false,
|
||||||
|
name,
|
||||||
|
onExitEditing,
|
||||||
|
onNameSave,
|
||||||
|
validateName,
|
||||||
|
}: EditableEntityNameProps) => {
|
||||||
|
const [
|
||||||
|
inputRef,
|
||||||
|
editableName,
|
||||||
|
validationError,
|
||||||
|
handleKeyUp,
|
||||||
|
handleTitleChange,
|
||||||
|
] = useEditableText(isEditing, name, onExitEditing, validateName, onNameSave);
|
||||||
|
|
||||||
|
const inputProps = useMemo(
|
||||||
|
() => ({
|
||||||
|
["data-testid"]: inputTestId,
|
||||||
|
onKeyUp: handleKeyUp,
|
||||||
|
onChange: handleTitleChange,
|
||||||
|
autoFocus: true,
|
||||||
|
style: {
|
||||||
|
paddingTop: 4,
|
||||||
|
paddingBottom: 4,
|
||||||
|
left: -1,
|
||||||
|
top: -5,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
[handleKeyUp, handleTitleChange, inputTestId],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Styled.Root>
|
||||||
|
{isLoading ? (
|
||||||
|
<Spinner size="sm" />
|
||||||
|
) : (
|
||||||
|
<Styled.IconContainer>{icon}</Styled.IconContainer>
|
||||||
|
)}
|
||||||
|
<Tooltip content={validationError} visible={Boolean(validationError)}>
|
||||||
|
<Styled.Text
|
||||||
|
aria-invalid={Boolean(validationError)}
|
||||||
|
inputProps={inputProps}
|
||||||
|
inputRef={inputRef}
|
||||||
|
isEditable={isEditing}
|
||||||
|
kind="body-s"
|
||||||
|
>
|
||||||
|
{editableName}
|
||||||
|
</Styled.Text>
|
||||||
|
</Tooltip>
|
||||||
|
</Styled.Root>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
import type React from "react";
|
||||||
|
|
||||||
|
export interface EditableEntityNameProps {
|
||||||
|
icon: React.ReactNode;
|
||||||
|
inputTestId?: string;
|
||||||
|
isEditing: boolean;
|
||||||
|
isLoading?: boolean;
|
||||||
|
name: string;
|
||||||
|
onExitEditing: () => void;
|
||||||
|
onNameSave: (name: string) => void;
|
||||||
|
validateName: (name: string) => string | null;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
export { EditableEntityName } from "./EditableEntityName";
|
||||||
|
|
@ -5,7 +5,7 @@ import { Tooltip } from "../../../Tooltip";
|
||||||
|
|
||||||
import type { EntityItemProps } from "./EntityItem.types";
|
import type { EntityItemProps } from "./EntityItem.types";
|
||||||
import { EntityEditableName } from "./EntityItem.styles";
|
import { EntityEditableName } from "./EntityItem.styles";
|
||||||
import { useEditableText } from "../Editable";
|
import { useEditableText } from "../../../__hooks__/useEditableText";
|
||||||
import clx from "classnames";
|
import clx from "classnames";
|
||||||
|
|
||||||
export const EntityItem = (props: EntityItemProps) => {
|
export const EntityItem = (props: EntityItemProps) => {
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,6 @@ export { EmptyState } from "./EmptyState";
|
||||||
export { NoSearchResults } from "./NoSearchResults";
|
export { NoSearchResults } from "./NoSearchResults";
|
||||||
export * from "./ExplorerContainer";
|
export * from "./ExplorerContainer";
|
||||||
export * from "./EntityItem";
|
export * from "./EntityItem";
|
||||||
export { useEditableText } from "./Editable";
|
export { useEditableText } from "../../__hooks__/useEditableText";
|
||||||
export * from "./EntityGroupsList";
|
export * from "./EntityGroupsList";
|
||||||
export * from "./EntityListTree";
|
export * from "./EntityListTree";
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
export * from "./IDEHeader";
|
export * from "./IDEHeader";
|
||||||
export * from "./EntityExplorer";
|
export * from "./EntityExplorer";
|
||||||
export * from "./Sidebar";
|
export * from "./Sidebar";
|
||||||
|
export * from "./EditableEntityName";
|
||||||
|
export * from "./EditableDismissibleTab";
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
export { useDOMRef } from "./useDomRef";
|
||||||
|
export { useEditableText } from "./useEditableText";
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { renderHook, act } from "@testing-library/react-hooks";
|
import { renderHook, act } from "@testing-library/react-hooks";
|
||||||
import { useEditableText } from "./useEditableText";
|
|
||||||
import { fireEvent, render } from "@testing-library/react";
|
import { fireEvent, render } from "@testing-library/react";
|
||||||
import { Text } from "../../..";
|
import { Text } from "../..";
|
||||||
|
|
||||||
|
import { useEditableText } from "./useEditableText";
|
||||||
|
|
||||||
describe("useEditableText", () => {
|
describe("useEditableText", () => {
|
||||||
const mockExitEditing = jest.fn();
|
const mockExitEditing = jest.fn();
|
||||||
|
|
@ -7,8 +7,10 @@ import {
|
||||||
useRef,
|
useRef,
|
||||||
type RefObject,
|
type RefObject,
|
||||||
} from "react";
|
} from "react";
|
||||||
|
|
||||||
import { usePrevious } from "@mantine/hooks";
|
import { usePrevious } from "@mantine/hooks";
|
||||||
import { useEventCallback, useEventListener } from "usehooks-ts";
|
import { useEventCallback, useEventListener } from "usehooks-ts";
|
||||||
|
|
||||||
import { normaliseName } from "./utils";
|
import { normaliseName } from "./utils";
|
||||||
|
|
||||||
export function useEditableText(
|
export function useEditableText(
|
||||||
|
|
@ -1,14 +1,16 @@
|
||||||
import "./__theme__/default/index.css";
|
import "./__theme__/default/index.css";
|
||||||
|
|
||||||
export * from "./AnnouncementPopover";
|
|
||||||
export * from "./AnnouncementModal";
|
export * from "./AnnouncementModal";
|
||||||
|
export * from "./AnnouncementPopover";
|
||||||
export * from "./Avatar";
|
export * from "./Avatar";
|
||||||
export * from "./Button";
|
export * from "./Badge";
|
||||||
export * from "./Banner";
|
export * from "./Banner";
|
||||||
|
export * from "./Button";
|
||||||
export * from "./Callout";
|
export * from "./Callout";
|
||||||
export * from "./Checkbox";
|
export * from "./Checkbox";
|
||||||
export * from "./Collapsible";
|
export * from "./Collapsible";
|
||||||
export * from "./DatePicker";
|
export * from "./DatePicker";
|
||||||
|
export * from "./DismissibleTab";
|
||||||
export * from "./Divider";
|
export * from "./Divider";
|
||||||
export * from "./Flex";
|
export * from "./Flex";
|
||||||
export * from "./FormControl";
|
export * from "./FormControl";
|
||||||
|
|
@ -31,10 +33,9 @@ export * from "./Switch";
|
||||||
export * from "./Tab";
|
export * from "./Tab";
|
||||||
export * from "./Table";
|
export * from "./Table";
|
||||||
export * from "./Tag";
|
export * from "./Tag";
|
||||||
|
export * from "./Templates";
|
||||||
export * from "./Text";
|
export * from "./Text";
|
||||||
export * from "./Toast";
|
export * from "./Toast";
|
||||||
export * from "./ToggleButton";
|
export * from "./ToggleButton";
|
||||||
export * from "./Tooltip";
|
|
||||||
export * from "./ToggleButtonGroup";
|
export * from "./ToggleButtonGroup";
|
||||||
export * from "./Templates";
|
export * from "./Tooltip";
|
||||||
export * from "./Badge";
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user