Skip to content

Commit

Permalink
feat: wrapped Tag and TagList
Browse files Browse the repository at this point in the history
* feat: wrapped Tag and TagList

* refactor: omit unneeded types

* fix: hide the MUI interface

* fix: adding types as needed

* refactor: ensure Tag is li if within a list

* refactor: make isInteractive implicit with onClick

* chore: fixing types and code style
  • Loading branch information
jordankoschei-okta authored Apr 21, 2023
1 parent 7f35e7f commit 43ab012
Show file tree
Hide file tree
Showing 7 changed files with 140 additions and 59 deletions.
41 changes: 41 additions & 0 deletions packages/odyssey-react-mui/src/Tag.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*!
* Copyright (c) 2022-present, Okta, Inc. and/or its affiliates. All rights reserved.
* The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the "License.")
*
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and limitations under the License.
*/

import { Chip, ChipProps } from "@mui/material";
import { memo, useContext } from "react";
import { TagListContext } from "./TagListContext";

export type TagProps = {
isDisabled?: boolean;
label: string;
onClick?: ChipProps["onClick"];
onRemove?: ChipProps["onDelete"];
};

const Tag = ({ isDisabled, label, onClick, onRemove }: TagProps) => {
const { chipElementType } = useContext(TagListContext);

return (
<Chip
label={label}
clickable={onClick ? true : false}
component={chipElementType}
disabled={isDisabled}
onClick={onClick}
onDelete={onRemove}
/>
);
};

const MemoizedTag = memo(Tag);

export { MemoizedTag as Tag };
43 changes: 43 additions & 0 deletions packages/odyssey-react-mui/src/TagList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*!
* Copyright (c) 2022-present, Okta, Inc. and/or its affiliates. All rights reserved.
* The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the "License.")
*
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and limitations under the License.
*/

import { Tag } from "./";
import { Stack } from "@mui/material";
import { memo, ReactElement, useMemo } from "react";
import { ChipElementType, TagListContext } from "./TagListContext";

export type TagListProps = {
children: ReactElement<typeof Tag> | Array<ReactElement<typeof Tag>>;
};

const TagList = ({ children }: TagListProps) => {
const providerValue = useMemo<{
chipElementType: ChipElementType;
}>(
() => ({
chipElementType: "li",
}),
[]
);

return (
<Stack component="ul" direction="row" spacing={2}>
<TagListContext.Provider value={providerValue}>
{children}
</TagListContext.Provider>
</Stack>
);
};

const MemoizedTagList = memo(TagList);

export { MemoizedTagList as TagList };
23 changes: 23 additions & 0 deletions packages/odyssey-react-mui/src/TagListContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*!
* Copyright (c) 2022-present, Okta, Inc. and/or its affiliates. All rights reserved.
* The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the "License.")
*
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and limitations under the License.
*/

import { createContext } from "react";

export type ChipElementType = "li" | "div";

export type TagListContextType = {
chipElementType: ChipElementType;
};

export const TagListContext = createContext<TagListContextType>({
chipElementType: "div",
});
2 changes: 2 additions & 0 deletions packages/odyssey-react-mui/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ export * from "./RadioGroup";
export * from "./ScreenReaderText";
export * from "./SearchField";
export * from "./Status";
export * from "./Tag";
export * from "./TagList";
export * from "./TextField";
export * from "./theme";
export * from "./ThemeProvider";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ TagList UI is simple. It consists of typography and spacing within a neutral con
<Story id="mui-components-tag--clickable" />
</Canvas>

### Deletable Tags
### Removable Tags

<Canvas>
<Story id="mui-components-tag--deletable" />
<Story id="mui-components-tag--removable" />
</Canvas>

### Disabled Tags
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,18 @@
* See the License for the specific language governing permissions and limitations under the License.
*/

import { Story } from "@storybook/react";
import { Chip, Stack } from "@okta/odyssey-react-mui";
import { Meta, Story } from "@storybook/react";
import { action } from "@storybook/addon-actions";
import { Tag, TagList, TagProps } from "@okta/odyssey-react-mui";
import { MuiThemeDecorator } from "../../../../.storybook/components";

import TagMdx from "./Tag.mdx";

export default {
const storybookMeta: Meta<TagProps> = {
title: `MUI Components/Tag`,
component: Chip,
component: Tag,
parameters: {
actions: { argTypesRegex: null },
docs: {
page: TagMdx,
},
Expand All @@ -29,83 +31,53 @@ export default {
control: "text",
defaultValue: "Starship",
},
label2: {
control: "text",
defaultValue: null,
},
label3: {
control: "text",
defaultValue: null,
},
clickable: {
isDisabled: {
control: "boolean",
defaultValue: false,
},
disabled: {
control: "boolean",
defaultValue: false,
onClick: {
control: "function",
},
deletable: {
control: "boolean",
defaultValue: false,
onRemove: {
control: "function",
},
},
decorators: [MuiThemeDecorator],
};

const handleDelete = () => {
console.info("You clicked the delete icon.");
export default storybookMeta;

const DefaultTemplate: Story<TagProps> = (args) => {
return <Tag {...args} />;
};

const DefaultTemplate: Story = (args) => {
const ListTemplate: Story<TagProps> = (args) => {
return (
<Stack direction="row" spacing={2}>
<Chip
label={args.label}
clickable={args.clickable}
disabled={args.disabled}
onDelete={args.deletable && handleDelete}
/>
{args.label2 && (
<Chip
label={args.label2}
clickable={args.clickable}
disabled={args.disabled}
onDelete={args.deletable && handleDelete}
/>
)}
{args.label3 && (
<Chip
label={args.label3}
clickable={args.clickable}
disabled={args.disabled}
onDelete={args.deletable && handleDelete}
/>
)}
</Stack>
<TagList>
<Tag {...args} />
<Tag label="Another tag" />
<Tag label="A third tag" />
</TagList>
);
};

export const Default = DefaultTemplate.bind({});
Default.args = {};

export const List = DefaultTemplate.bind({});
List.args = {
label2: "Warp-capable",
label3: "Unmanned",
};
export const List = ListTemplate.bind({});
List.args = {};

export const Clickable = DefaultTemplate.bind({});
Clickable.args = {
clickable: true,
onClick: action("clicked"),
};

export const Deletable = DefaultTemplate.bind({});
Deletable.args = {
deletable: true,
export const Removable = DefaultTemplate.bind({});
Removable.args = {
onRemove: action("removed"),
};

export const Disabled = DefaultTemplate.bind({});
Disabled.args = {
disabled: true,
isDisabled: true,
};
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ const DefaultTemplate: Story = (args) => {
return (
<>
<Button variant="primary" onClick={openToast}>
Open {args.severity} snackbar Open {args.severity} toast
Open {args.severity} toast
</Button>
<Stack spacing={2} sx={{ width: "100%" }}>
<Snackbar
Expand Down

0 comments on commit 43ab012

Please sign in to comment.