diff --git a/packages/react/.storybook/story-config.ts b/packages/react/.storybook/story-config.ts index 88343643..788d4852 100644 --- a/packages/react/.storybook/story-config.ts +++ b/packages/react/.storybook/story-config.ts @@ -32,6 +32,7 @@ enum StorybookCategories { } export type Stories = + | 'AccountOverview' | 'ActionCard' | 'AppBar' | 'AppShell' @@ -77,8 +78,12 @@ export type Stories = | 'Navbar' | 'OutlinedInput' | 'PhoneNumberInput' + | 'Select' | 'SignIn' | 'Stepper' + | 'Tab' + | 'TabPanel' + | 'Tabs' | 'TextField' | 'Toolbar' | 'Tooltip' @@ -106,6 +111,9 @@ export type StorybookConfig = Record< >; const StoryConfig: StorybookConfig = { + AccountOverview: { + hierarchy: `${StorybookCategories.Patterns}/Account Overview`, + }, ActionCard: { hierarchy: `${StorybookCategories.Surfaces}/Action Card`, }, @@ -246,12 +254,24 @@ const StoryConfig: StorybookConfig = { PhoneNumberInput: { hierarchy: `${StorybookCategories.Inputs}/Phone Number Input`, }, + Select: { + hierarchy: `${StorybookCategories.Inputs}/Select`, + }, SignIn: { hierarchy: `${StorybookCategories.Patterns}/Sign In`, }, Stepper: { hierarchy: `${StorybookCategories.Surfaces}/Stepper`, }, + Tab: { + hierarchy: `${StorybookCategories.Navigation}/Tab`, + }, + TabPanel: { + hierarchy: `${StorybookCategories.Navigation}/Tab Panel`, + }, + Tabs: { + hierarchy: `${StorybookCategories.Navigation}/Tabs`, + }, TextField: { hierarchy: `${StorybookCategories.Inputs}/Text Field`, }, diff --git a/packages/react/src/components/AccountOverview/AccountOverview.stories.mdx b/packages/react/src/components/AccountOverview/AccountOverview.stories.mdx new file mode 100644 index 00000000..ae861b70 --- /dev/null +++ b/packages/react/src/components/AccountOverview/AccountOverview.stories.mdx @@ -0,0 +1,160 @@ +import {ArgsTable, Source, Story, Canvas, Meta} from '@storybook/addon-docs'; +import AccountOverview from './AccountOverview.tsx'; +import Button from '../Button'; +import dedent from 'ts-dedent'; +import StoryConfig from '../../../.storybook/story-config.ts'; +import {withDesign} from '../../../.storybook/utils.ts'; +import Typography from '../Typography'; + +export const meta = { + component: AccountOverview, + title: StoryConfig.AccountOverview.hierarchy, +}; + + + +export const Template = args => ; + +# AccountOverview + +- [Overview](#overview) +- [Props](#props) +- [Usage](#usage) +- [Variants](#variants) + - [Incomplete](#incomplete) + - [Complete](#complete) + +## Overview + +This component is used to display the user's account overview. It includes the user's profile picture, name, email, account progress and account completion steps. + + + Welcome Mathew, + subheader: Manage your personal information, account security and privacy settings., + user:{ + image: '/assets/images/avatar-john.svg', + name: 'Matthew', + email: 'matthew@wso2.com' + }, + accountProgress: 60, + accountCompletionStepsTitle: "Complete your Profile. It's at 60%", + accountCompletionSteps: [ + { + title: 'Add your email address', + description: 'You can add your email address to your profile to receive notifications and updates from us.', + illustration: carousel illustration, + action: + }, + { + title: 'Add your phone number', + description: 'You can add your phone number to your profile to receive notifications and updates from us.', + illustration: carousel illustration, + action: + }, + { + title: 'Add your address', + description: 'You can add your address to your profile to receive notifications and updates from us.', + illustration: carousel illustration, + action: + } + ], + }} + > + {Template.bind({})} + + + +## Props + + + +## Usage + +Import and use the `AccountOverview` component in your components as follows. + +Welcome Mathew} + accountProgress={60} + /> +}`} +/> + +## Variants + +### Incomplete + + + Welcome Mathew, + subheader: Manage your personal information, account security and privacy settings., + user:{ + image: '/assets/images/avatar-john.svg', + name: 'Matthew', + email: 'matthew@wso2.com' + }, + accountProgress: 60, + accountCompletionStepsTitle: "Complete your Profile. It's at 60%", + accountCompletionSteps: [ + { + title: 'Add your email address', + description: 'You can add your email address to your profile to receive notifications and updates from us.', + illustration: carousel illustration, + action: + }, + { + title: 'Add your phone number', + description: 'You can add your phone number to your profile to receive notifications and updates from us.', + illustration: carousel illustration, + action: + }, + { + title: 'Add your address', + description: 'You can add your address to your profile to receive notifications and updates from us.', + illustration: carousel illustration, + action: + } + ], + }} + > + {Template.bind({})} + + + +### Complete + + + Welcome Mathew, + subheader: Manage your personal information, account security and privacy settings., + user:{ + image: '/assets/images/avatar-john.svg', + name: 'Matthew', + email: 'matthew@wso2.com' + }, + accountCompletionStepsTitle: "Complete your Profile. It's at 60%", + accountProgress: 100, + illustration: image + }} + > + {Template.bind({})} + + diff --git a/packages/react/src/components/AccountOverview/AccountOverview.tsx b/packages/react/src/components/AccountOverview/AccountOverview.tsx new file mode 100644 index 00000000..bcd68a02 --- /dev/null +++ b/packages/react/src/components/AccountOverview/AccountOverview.tsx @@ -0,0 +1,112 @@ +/** + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). All Rights Reserved. + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with 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 clsx from 'clsx'; +import {FC, ReactElement, ReactNode} from 'react'; +import {WithWrapperProps} from '../../models'; +import {composeComponentDisplayName} from '../../utils'; +import Box from '../Box'; +import Card, {CardProps} from '../Card'; +import CardHeader, {CardHeaderProps} from '../CardHeader'; +import {Carousel, CarouselStep} from '../Carousel'; +import CircularProgressAvatar from '../CircularProgressAvatar'; +import Divider from '../Divider'; +import {UserTemplate} from '../UserDropdownMenu'; +import './account-overview.scss'; + +export interface AccountOverviewProps extends Omit { + /** + * Account completion steps. + */ + accountCompletionSteps?: AccountCompletionSteps[]; + /** + * Account completion steps title. + */ + accountCompletionStepsTitle?: string; + /** + * Account progress. + */ + accountProgress: number; + /** + * Card header props. + */ + cardHeaderProps?: CardHeaderProps; + /** + * Card Subheader. + * @example subheader + */ + subheader?: ReactNode; + /** + * Card Title. + * @example title + */ + title: ReactNode; + /** + * Logged user information. + */ + user: UserTemplate; +} + +export type AccountCompletionSteps = CarouselStep; + +const COMPONENT_NAME: string = 'AccountOverview'; + +const AccountOverview: FC & WithWrapperProps = (props: AccountOverviewProps): ReactElement => { + const { + className, + title, + subheader, + accountCompletionStepsTitle, + accountCompletionSteps, + accountProgress, + user, + cardHeaderProps, + ...rest + } = props; + + const classes: string = clsx('oxygen-account-overview', className); + + return ( + + + } + title={title} + subheader={subheader} + {...cardHeaderProps} + /> + {accountCompletionSteps && ( + + + + + )} + + ); +}; + +AccountOverview.displayName = composeComponentDisplayName(COMPONENT_NAME); +AccountOverview.muiName = COMPONENT_NAME; + +export default AccountOverview; diff --git a/packages/react/src/components/AccountOverview/account-overview.scss b/packages/react/src/components/AccountOverview/account-overview.scss new file mode 100644 index 00000000..e57794a6 --- /dev/null +++ b/packages/react/src/components/AccountOverview/account-overview.scss @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). All Rights Reserved. + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with 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. + */ + +.oxygen-account-overview { + padding: 0.8rem 1rem; + + .oxygen-card-header { + padding: 1rem 0; + padding-bottom: 1.5rem; + } + + .oxygen-account-completion-steps-box { + padding: 0.5rem; + } +} diff --git a/packages/react/src/components/AccountOverview/index.ts b/packages/react/src/components/AccountOverview/index.ts new file mode 100644 index 00000000..e2c2d393 --- /dev/null +++ b/packages/react/src/components/AccountOverview/index.ts @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). All Rights Reserved. + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with 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. + */ + +export {default} from './AccountOverview'; +export type {AccountOverviewProps} from './AccountOverview'; diff --git a/packages/react/src/components/Avatar/Avatar.tsx b/packages/react/src/components/Avatar/Avatar.tsx index f5d76c06..e841274f 100644 --- a/packages/react/src/components/Avatar/Avatar.tsx +++ b/packages/react/src/components/Avatar/Avatar.tsx @@ -30,7 +30,7 @@ const COMPONENT_NAME: string = 'Avatar'; const Avatar: FC & WithWrapperProps = (props: AvatarProps): ReactElement => { const {className, ...rest} = props; - const classes: string = clsx('oxygen-ui-avatar', className); + const classes: string = clsx('oxygen-avatar', className); return ; }; diff --git a/packages/react/src/components/Avatar/__tests__/__snapshots__/Avatar.test.tsx.snap b/packages/react/src/components/Avatar/__tests__/__snapshots__/Avatar.test.tsx.snap index 1e7c2e7d..43e0fbcd 100644 --- a/packages/react/src/components/Avatar/__tests__/__snapshots__/Avatar.test.tsx.snap +++ b/packages/react/src/components/Avatar/__tests__/__snapshots__/Avatar.test.tsx.snap @@ -4,7 +4,7 @@ exports[`Avatar should match the snapshot 1`] = `
carousel illustration, - buttonText: "Add first name", - onButtonClick: ()=>{} }, { + action: , title: "What is your last name?", description: "Start theming journey with Oxygen UI", illustration: carousel illustration, - buttonText: "Add last name", - onButtonClick: ()=>{} } ], - title: "Complete your profile. It’s at 60%" + title: Complete your Profile. It’s at 60% }} > {Template.bind({})} @@ -69,11 +68,10 @@ function Demo() { {}}>Add Last Name, title: "What is your first name?", description: "Start theming journey with Oxygen UI", illustration: carousel illustration, - buttonText: "Add first name", - onButtonClick: ()=>{} } ]} /> diff --git a/packages/react/src/components/Carousel/Carousel.tsx b/packages/react/src/components/Carousel/Carousel.tsx index cd599fea..f3c11de3 100644 --- a/packages/react/src/components/Carousel/Carousel.tsx +++ b/packages/react/src/components/Carousel/Carousel.tsx @@ -18,7 +18,7 @@ import {ChevronLeftIcon, ChevronRightIcon} from '@oxygen-ui/react-icons'; import clsx from 'clsx'; -import {FC, HTMLAttributes, ReactElement, useEffect, useMemo, useState} from 'react'; +import {FC, HTMLAttributes, ReactElement, ReactNode, useEffect, useMemo, useState} from 'react'; import {useIsMobile} from '../../hooks'; import {WithWrapperProps} from '../../models'; import {composeComponentDisplayName} from '../../utils'; @@ -28,34 +28,36 @@ import Card from '../Card'; import CardContent from '../CardContent'; import IconButton from '../IconButton'; import {IconButtonVariants} from '../IconButton/IconButton'; +import ListItem from '../ListItem'; +import ListItemIcon from '../ListItemIcon'; +import ListItemText from '../ListItemText'; import {Stepper} from '../Stepper'; -import Typography from '../Typography'; import './carousel.scss'; -interface CarouselStep { +export interface CarouselStep { /** - * The text to be displayed in the button. + * Action to be performed on the step. + * @example */ - buttonText: string; + action?: ReactNode; /** * The description of the step. + * @example description */ - description: string; + description?: ReactNode; /** * The illustration to be displayed in the step. + * @example icon */ - illustration: ReactElement; - /** - * Callback method to be called when the button is clicked. - */ - onButtonClick: () => void; + illustration?: ReactElement; /** * The title of the step. + * @example title */ - title: string; + title: ReactNode; } -export interface CarouselProps extends HTMLAttributes { +export interface CarouselProps extends Omit, 'title'> { /** * Specifies whether to auto play the carousel. */ @@ -78,8 +80,9 @@ export interface CarouselProps extends HTMLAttributes { steps: CarouselStep[]; /** * The title of the carousel. + * @example title */ - title?: string; + title?: ReactNode; } const COMPONENT_NAME: string = 'Carousel'; @@ -125,74 +128,63 @@ const Carousel: FC & WithWrapperProps = (props: CarouselProps): R }; const generateCarouselSteps = (): ReactElement[] => - steps.map((step: CarouselStep) => ( - - - - {step.illustration} - - - {step.title} - - - {step.description} - - - - - - + steps?.map((step: CarouselStep) => { + const {title: stepTitle, description, illustration, action} = step; + return ( + + + + {illustration && {illustration}} + + + + {action && {action}} - - )); + ); + }); return ( - {title && {title}} - {isMobile ? ( - - - - - - - - - ) : ( - - - - - )} + {title} + + + + + + + + + + + + diff --git a/packages/react/src/components/Carousel/carousel.scss b/packages/react/src/components/Carousel/carousel.scss index 408b3450..0ddc5477 100644 --- a/packages/react/src/components/Carousel/carousel.scss +++ b/packages/react/src/components/Carousel/carousel.scss @@ -18,16 +18,17 @@ .oxygen-carousel { position: relative; - width: 100%; .oxygen-carousel-top-bar { display: flex; justify-content: space-between; + align-items: center; .oxygen-carousel-title { flex-grow: 1; align-items: center; display: flex; + padding: 1rem 0; } .oxygen-carousel-button-group { @@ -38,28 +39,34 @@ } } - .oxygen-carousel-step-card { - padding: 0; - } + .oxygen-carousel-step { + .oxygen-card { + padding: 0.5rem 1rem; - .oxygen-carousel-step-card-content { - display: flex; - justify-content: flex-start; - gap: 1.5em; - } + .oxygen-list-item { + gap: 1rem; + } + } - .oxygen-carousel-step-button-bar { - display: flex; - justify-content: flex-end; - padding-top: 1em; + .oxygen-carousel-step-button-bar { + padding-top: 1rem; + display: flex; + justify-content: flex-end; + } } .oxygen-carousel-mobile-buttons { - display: flex; - justify-content: space-between; - z-index: 1; - position: absolute; - width: 100%; - bottom: calc(42px + 1em); + display: none; + } + + &.mobile { + .oxygen-carousel-button-group { + display: none; + } + + .oxygen-carousel-mobile-buttons { + display: flex; + gap: 1rem; + } } } diff --git a/packages/react/src/components/Carousel/index.ts b/packages/react/src/components/Carousel/index.ts index ffd62680..7eda8a25 100644 --- a/packages/react/src/components/Carousel/index.ts +++ b/packages/react/src/components/Carousel/index.ts @@ -18,3 +18,4 @@ export {default as Carousel} from './Carousel'; export type {CarouselProps} from './Carousel'; +export type {CarouselStep} from './Carousel'; diff --git a/packages/react/src/components/CircularProgressAvatar/CircularProgressAvatar.tsx b/packages/react/src/components/CircularProgressAvatar/CircularProgressAvatar.tsx index 6498ee19..726f4444 100644 --- a/packages/react/src/components/CircularProgressAvatar/CircularProgressAvatar.tsx +++ b/packages/react/src/components/CircularProgressAvatar/CircularProgressAvatar.tsx @@ -52,7 +52,6 @@ const CircularProgressAvatar: FC & WithWrapperProps return (
U
diff --git a/packages/react/src/components/Select/Select.stories.mdx b/packages/react/src/components/Select/Select.stories.mdx new file mode 100644 index 00000000..b1fd5271 --- /dev/null +++ b/packages/react/src/components/Select/Select.stories.mdx @@ -0,0 +1,69 @@ +import {ArgsTable, Source, Story, Canvas, Meta} from '@storybook/addon-docs'; +import {useArgs} from '@storybook/client-api'; +import dedent from 'ts-dedent'; +import StoryConfig from '../../../.storybook/story-config.ts'; +import Select from './Select.tsx'; +import MenuItem from '../MenuItem'; + +export const meta = { + component: Select, + title: StoryConfig.Select.hierarchy, +}; + + + +export const Template = args => { + const [{open, anchor, onChange, label}, updateArgs] = useArgs(); + return ( + + ) +}; + +# Select + +- [Overview](#overview) +- [Props](#props) +- [Usage](#usage) + +## Overview + +The `Select` component is used to create a select input. + + + + {Template.bind({})} + + + +## Props + + + +## Usage + +Import and use the `Select` component in your components as follows. + + + Ten + Twenty + Thirty + + ); +}`} +/> diff --git a/packages/react/src/components/Select/Select.tsx b/packages/react/src/components/Select/Select.tsx new file mode 100644 index 00000000..fada4c25 --- /dev/null +++ b/packages/react/src/components/Select/Select.tsx @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). All Rights Reserved. + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with 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 MuiSelect, {SelectProps as MuiSelectProps} from '@mui/material/Select'; +import clsx from 'clsx'; +import {forwardRef, ForwardRefExoticComponent, MutableRefObject, ReactElement} from 'react'; +import {WithWrapperProps} from '../../models'; +import {composeComponentDisplayName} from '../../utils'; +import Box from '../Box'; +import InputLabel, {InputLabelProps as MuiInputLabelProps} from '../InputLabel'; +import './select.scss'; + +export interface SelectProps extends MuiSelectProps { + InputLabelProps?: MuiInputLabelProps; +} + +const COMPONENT_NAME: string = 'Select'; + +const Select: ForwardRefExoticComponent & WithWrapperProps = forwardRef( + (props: SelectProps, ref: MutableRefObject): ReactElement => { + const {className, InputLabelProps, label, id, ...rest} = props; + + const classes: string = clsx('oxygen-select', className); + + return ( + + {label && ( + + {label} + + )} + + + ); + }, +) as ForwardRefExoticComponent & WithWrapperProps; + +Select.displayName = composeComponentDisplayName(COMPONENT_NAME); +Select.muiName = COMPONENT_NAME; +Select.defaultProps = {}; + +export default Select; diff --git a/packages/react/src/components/Select/__tests__/Select.test.tsx b/packages/react/src/components/Select/__tests__/Select.test.tsx new file mode 100644 index 00000000..fba25597 --- /dev/null +++ b/packages/react/src/components/Select/__tests__/Select.test.tsx @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). All Rights Reserved. + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with 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 {render} from '@unit-testing'; +import Select from '../Select'; + +describe('Select', () => { + it('should render successfully', () => { + const {baseElement} = render(); + expect(baseElement).toMatchSnapshot(); + }); +}); diff --git a/packages/react/src/components/Select/__tests__/__snapshots__/Select.test.tsx.snap b/packages/react/src/components/Select/__tests__/__snapshots__/Select.test.tsx.snap new file mode 100644 index 00000000..2d97e3cc --- /dev/null +++ b/packages/react/src/components/Select/__tests__/__snapshots__/Select.test.tsx.snap @@ -0,0 +1,60 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Select should match the snapshot 1`] = ` + +
+
+
+ + + + +
+
+
+ +`; diff --git a/packages/react/src/components/Select/index.ts b/packages/react/src/components/Select/index.ts new file mode 100644 index 00000000..3f39c83e --- /dev/null +++ b/packages/react/src/components/Select/index.ts @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). All Rights Reserved. + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with 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. + */ + +export {default} from './Select'; +export type {SelectProps} from './Select'; diff --git a/packages/react/src/components/Select/select.scss b/packages/react/src/components/Select/select.scss new file mode 100644 index 00000000..78b09832 --- /dev/null +++ b/packages/react/src/components/Select/select.scss @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). All Rights Reserved. + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with 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. + */ + +.oxygen-select { + /* Add Styles */ +} diff --git a/packages/react/src/components/Tab/Tab.stories.mdx b/packages/react/src/components/Tab/Tab.stories.mdx new file mode 100644 index 00000000..f7c751b0 --- /dev/null +++ b/packages/react/src/components/Tab/Tab.stories.mdx @@ -0,0 +1,55 @@ +import {ArgsTable, Source, Story, Canvas, Meta} from '@storybook/addon-docs'; +import Tabs from '@mui/material/Tabs'; +import dedent from 'ts-dedent'; +import StoryConfig from '../../../.storybook/story-config.ts'; +import Tab from './Tab.tsx'; + +export const meta = { + component: Tab, + title: StoryConfig.Tab.hierarchy, +}; + + + +export const Template = (args) => { + return ( + + + + ) +}; + +# Tab + +- [Overview](#overview) +- [Props](#props) +- [Usage](#usage) + +## Overview + +Tab component can be used with Tabs component to implement navigation between different views or sections. + + + + {Template.bind({})} + + + +## Props + + + +## Usage + +Import and use the `Tab` component in your components as follows. + +; +}`} +/> diff --git a/packages/react/src/components/Tab/Tab.tsx b/packages/react/src/components/Tab/Tab.tsx new file mode 100644 index 00000000..33d5cea9 --- /dev/null +++ b/packages/react/src/components/Tab/Tab.tsx @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). All Rights Reserved. + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with 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 MuiTab, {TabProps as MuiTabProps} from '@mui/material/Tab'; +import clsx from 'clsx'; +import {forwardRef, ForwardRefExoticComponent, MutableRefObject, ReactElement} from 'react'; +import {WithWrapperProps} from '../../models'; +import {composeComponentDisplayName} from '../../utils'; +import './tab.scss'; + +export type TabProps = MuiTabProps; + +const COMPONENT_NAME: string = 'Tab'; + +const Tab: ForwardRefExoticComponent & WithWrapperProps = forwardRef( + (props: TabProps, ref: MutableRefObject): ReactElement => { + const {className, ...rest} = props; + + const classes: string = clsx('oxygen-tab', className); + + return ; + }, +) as ForwardRefExoticComponent & WithWrapperProps; + +Tab.displayName = composeComponentDisplayName(COMPONENT_NAME); +Tab.muiName = COMPONENT_NAME; + +export default Tab; diff --git a/packages/react/src/components/Tab/__tests__/Tab.test.tsx b/packages/react/src/components/Tab/__tests__/Tab.test.tsx new file mode 100644 index 00000000..030ac393 --- /dev/null +++ b/packages/react/src/components/Tab/__tests__/Tab.test.tsx @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). All Rights Reserved. + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with 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 {render} from '@unit-testing'; +import Tab from '../Tab'; + +describe('Tab', () => { + it('should render successfully', () => { + const {baseElement} = render(); + expect(baseElement).toBeTruthy(); + }); + + it('should match the snapshot', () => { + const {baseElement} = render(); + expect(baseElement).toMatchSnapshot(); + }); +}); diff --git a/packages/react/src/components/Tab/__tests__/__snapshots__/Tab.test.tsx.snap b/packages/react/src/components/Tab/__tests__/__snapshots__/Tab.test.tsx.snap new file mode 100644 index 00000000..c12c387a --- /dev/null +++ b/packages/react/src/components/Tab/__tests__/__snapshots__/Tab.test.tsx.snap @@ -0,0 +1,18 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Tab should match the snapshot 1`] = ` + +
+ +
+ +`; diff --git a/packages/react/src/components/Tab/index.ts b/packages/react/src/components/Tab/index.ts new file mode 100644 index 00000000..add4be5f --- /dev/null +++ b/packages/react/src/components/Tab/index.ts @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). All Rights Reserved. + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with 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. + */ + +export {default} from './Tab'; +export type {TabProps} from './Tab'; diff --git a/packages/react/src/components/Tab/tab.scss b/packages/react/src/components/Tab/tab.scss new file mode 100644 index 00000000..5b038e94 --- /dev/null +++ b/packages/react/src/components/Tab/tab.scss @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). All Rights Reserved. + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with 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. + */ + +.oxygen-tab { + /** Add Styles */ +} diff --git a/packages/react/src/components/TabPanel/TabPanel.stories.mdx b/packages/react/src/components/TabPanel/TabPanel.stories.mdx new file mode 100644 index 00000000..da519357 --- /dev/null +++ b/packages/react/src/components/TabPanel/TabPanel.stories.mdx @@ -0,0 +1,52 @@ +import {ArgsTable, Source, Story, Canvas, Meta} from '@storybook/addon-docs'; +import dedent from 'ts-dedent'; +import StoryConfig from '../../../.storybook/story-config.ts'; +import TabPanel from './TabPanel.tsx'; + +export const meta = { + component: TabPanel, + title: StoryConfig.TabPanel.hierarchy, +}; + + + +export const Template = args => ; + +# TabPanel + +- [Overview](#overview) +- [Props](#props) +- [Usage](#usage) + +## Overview + +TabPanel component can be used with Tabs component to implement the content of the tab views. + + + + {Template.bind({})} + + + +## Props + + + +## Usage + +Import and use the `TabPanel` component in your components as follows. + + + Sample Tab Panel + + ); +}`} +/> diff --git a/packages/react/src/components/TabPanel/TabPanel.tsx b/packages/react/src/components/TabPanel/TabPanel.tsx new file mode 100644 index 00000000..0849567a --- /dev/null +++ b/packages/react/src/components/TabPanel/TabPanel.tsx @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). All Rights Reserved. + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with 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 {BoxProps as MuiBoxProps} from '@mui/material'; +import clsx from 'clsx'; +import {forwardRef, ForwardRefExoticComponent, MutableRefObject, ReactElement} from 'react'; +import {WithWrapperProps} from '../../models'; +import {composeComponentDisplayName} from '../../utils'; +import Box from '../Box'; +import './tab-panel.scss'; + +export interface TabPanelProps extends MuiBoxProps { + /* + * The index of the corresponding `TabPanel`. + */ + index: number; + /* + * The value of the selected `TabPanel`. + */ + value: number; +} + +const COMPONENT_NAME: string = 'TabPanel'; + +const TabPanel: ForwardRefExoticComponent & WithWrapperProps = forwardRef( + (props: TabPanelProps, ref: MutableRefObject): ReactElement => { + const {className, children, value, index, ...rest} = props; + + const classes: string = clsx('oxygen-tab-panel', className); + + return ( + + ); + }, +) as ForwardRefExoticComponent & WithWrapperProps; + +TabPanel.displayName = composeComponentDisplayName(COMPONENT_NAME); +TabPanel.muiName = COMPONENT_NAME; + +export default TabPanel; diff --git a/packages/react/src/components/TabPanel/__tests__/TabPanel.test.tsx b/packages/react/src/components/TabPanel/__tests__/TabPanel.test.tsx new file mode 100644 index 00000000..a6db4e30 --- /dev/null +++ b/packages/react/src/components/TabPanel/__tests__/TabPanel.test.tsx @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). All Rights Reserved. + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with 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 {render} from '@unit-testing'; +import TabPanel from '../TabPanel'; + +describe('TabPanel', () => { + it('should render successfully', () => { + const {baseElement} = render(); + expect(baseElement).toBeTruthy(); + }); + + it('should match the snapshot', () => { + const {baseElement} = render(); + expect(baseElement).toMatchSnapshot(); + }); +}); diff --git a/packages/react/src/components/TabPanel/__tests__/__snapshots__/TabPanel.test.tsx.snap b/packages/react/src/components/TabPanel/__tests__/__snapshots__/TabPanel.test.tsx.snap new file mode 100644 index 00000000..d42eef6b --- /dev/null +++ b/packages/react/src/components/TabPanel/__tests__/__snapshots__/TabPanel.test.tsx.snap @@ -0,0 +1,16 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`TabPanel should match the snapshot 1`] = ` + +
+
+
+
+
+ +`; diff --git a/packages/react/src/components/TabPanel/index.ts b/packages/react/src/components/TabPanel/index.ts new file mode 100644 index 00000000..60dfa08e --- /dev/null +++ b/packages/react/src/components/TabPanel/index.ts @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). All Rights Reserved. + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with 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. + */ + +export {default} from './TabPanel'; +export type {TabPanelProps} from './TabPanel'; diff --git a/packages/react/src/components/TabPanel/tab-panel.scss b/packages/react/src/components/TabPanel/tab-panel.scss new file mode 100644 index 00000000..c918ff03 --- /dev/null +++ b/packages/react/src/components/TabPanel/tab-panel.scss @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). All Rights Reserved. + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with 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. + */ + +.oxygen-tab-panel { + padding: 1rem; +} diff --git a/packages/react/src/components/Tabs/Tabs.stories.mdx b/packages/react/src/components/Tabs/Tabs.stories.mdx new file mode 100644 index 00000000..e704f57a --- /dev/null +++ b/packages/react/src/components/Tabs/Tabs.stories.mdx @@ -0,0 +1,86 @@ +import {ArgsTable, Source, Story, Canvas, Meta} from '@storybook/addon-docs'; +import {useArgs} from '@storybook/client-api'; +import dedent from 'ts-dedent'; +import StoryConfig from '../../../.storybook/story-config.ts'; +import Tabs from './Tabs.tsx'; +import Tab from '../Tab'; + +export const meta = { + component: Tabs, + title: StoryConfig.Tabs.hierarchy, +}; + + + +export const Template = args => { + const [{value}, updateArgs] = useArgs(); + const handleChange = (event, newValue) => { + updateArgs({value: newValue}); + }; + return ( + + + + + + + + ); +}; + +# Tabs + +- [Overview](#overview) +- [Props](#props) +- [Usage](#usage) +- [Variants](#variants) + +## Overview + +Tabs component can be used to implement navigation between different views or sections at the same level of hierarchy. + + + + {Template.bind({})} + + + +## Props + + + +## Usage + +Import and use the `Tabs` component in your components as follows. + + + + + + + + + ); +}`} +/> + +## Variants + +### Scrollable + +Variation that makes the tabs header scrollable based on the viewport width. + + + + {Template.bind({})} + + diff --git a/packages/react/src/components/Tabs/Tabs.tsx b/packages/react/src/components/Tabs/Tabs.tsx new file mode 100644 index 00000000..a51fd4a9 --- /dev/null +++ b/packages/react/src/components/Tabs/Tabs.tsx @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). All Rights Reserved. + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with 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 MuiTabs, {TabsProps as MuiTabsProps} from '@mui/material/Tabs'; +import clsx from 'clsx'; +import {ElementType, forwardRef, ForwardRefExoticComponent, MutableRefObject, ReactElement} from 'react'; +import {WithWrapperProps} from '../../models'; +import {composeComponentDisplayName} from '../../utils'; +import Box from '../Box'; +import Divider from '../Divider'; +import './tabs.scss'; + +export type TabsProps = { + component?: C; +} & Omit, 'component'>; + +const COMPONENT_NAME: string = 'Tabs'; + +const Tabs: ForwardRefExoticComponent & WithWrapperProps = forwardRef( + (props: TabsProps, ref: MutableRefObject): ReactElement => { + const {className, ...rest} = props; + + const classes: string = clsx('oxygen-tabs', className); + + return ( + + + + + ); + }, +) as ForwardRefExoticComponent & WithWrapperProps; + +Tabs.displayName = composeComponentDisplayName(COMPONENT_NAME); +Tabs.muiName = COMPONENT_NAME; + +export default Tabs; diff --git a/packages/react/src/components/Tabs/__test__/Tabs.test.tsx b/packages/react/src/components/Tabs/__test__/Tabs.test.tsx new file mode 100644 index 00000000..356d259f --- /dev/null +++ b/packages/react/src/components/Tabs/__test__/Tabs.test.tsx @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). All Rights Reserved. + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with 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 {render} from '@unit-testing'; +import Tabs from '../Tabs'; + +describe('Tabs', () => { + it('should render successfully', () => { + const {baseElement} = render(); + expect(baseElement).toBeTruthy(); + }); + + it('should match the snapshot', () => { + const {baseElement} = render(); + expect(baseElement).toMatchSnapshot(); + }); +}); diff --git a/packages/react/src/components/Tabs/__test__/__snapshots__/Tabs.test.tsx.snap b/packages/react/src/components/Tabs/__test__/__snapshots__/Tabs.test.tsx.snap new file mode 100644 index 00000000..b4c8ff9b --- /dev/null +++ b/packages/react/src/components/Tabs/__test__/__snapshots__/Tabs.test.tsx.snap @@ -0,0 +1,32 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Tabs should match the snapshot 1`] = ` + +
+
+
+
+
+ +
+
+
+
+
+ +`; diff --git a/packages/react/src/components/Tabs/index.ts b/packages/react/src/components/Tabs/index.ts new file mode 100644 index 00000000..bc384c1a --- /dev/null +++ b/packages/react/src/components/Tabs/index.ts @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). All Rights Reserved. + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with 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. + */ + +export {default} from './Tabs'; +export type {TabsProps} from './Tabs'; diff --git a/packages/react/src/components/Tabs/tabs.scss b/packages/react/src/components/Tabs/tabs.scss new file mode 100644 index 00000000..5f5a4bbe --- /dev/null +++ b/packages/react/src/components/Tabs/tabs.scss @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). All Rights Reserved. + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with 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. + */ + +.oxygen-tabs { + /** Add Styles */ +}