Skip to content

Commit

Permalink
Merge pull request #79 from KasperskyLab/fix/tabs-fixes
Browse files Browse the repository at this point in the history
fix: tab refactoring, fix localization in 'more' tabs. fix hiding the last tab when it is placed
  • Loading branch information
gitpoeble authored Aug 7, 2024
2 parents 812e1a6 + 0bcd229 commit 593734b
Show file tree
Hide file tree
Showing 7 changed files with 407 additions and 172 deletions.
17 changes: 11 additions & 6 deletions packages/kaspersky-components/helpers/useIntersectionChildren.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
import { RefObject, useMemo } from 'react'
import { useResizeObserver } from './useResizeObserver'

/** Find position last inside element */
export const useIntersectionChildren = (ref: RefObject<Element>, padding = 0): number | undefined => {
/** The hook calculates the intersection of the container and its children, returns the index of last fitting child
@param ref External container ref
@param padding Padding to consider when intersecting
@param wrapperQuerySelector selector of internal container
@param renderCounter flag to trigger the recalculation
*/
export const useIntersectionChildren = (ref: RefObject<Element>, padding = 0, wrapperQuerySelector?: string, renderCounter?: number): number | undefined => {
const { right: containerRight } = useResizeObserver(ref) ?? { right: 0 }

return useMemo<number | undefined>(() => {
const container = ref.current
if (container === null) return undefined
const container = wrapperQuerySelector ? ref.current?.querySelector(wrapperQuerySelector) : ref.current
if (container === null || container === undefined) return undefined

const children = new Array(container.children.length)
.fill(null)
.map((_, i) => container.children[i])

const res = children.findIndex((child) => child.getBoundingClientRect().right + padding > containerRight)
return res === -1 ? undefined : Math.max(res - 1, 0)
}, [containerRight, ref])
}, [containerRight, ref, padding, renderCounter])
}
1 change: 1 addition & 0 deletions packages/kaspersky-components/src/indicator/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './Indicator'
export * from './types'
261 changes: 203 additions & 58 deletions packages/kaspersky-components/src/tabs/Tabs.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
import React from 'react'
import { useLocalization } from '@helpers/localization/useLocalization'
import { badges } from '@sb/badges'
import { withMeta } from '@sb/components/Meta'
import { sbHideControls } from '@sb/helpers'
import { Button } from '@src/button'
import { IndicatorMode } from '@src/indicator'
import { IndicatorModes } from '@src/indicator/types'
import { Textbox } from '@src/input'
import { Space } from '@src/space'
import { H3 } from '@src/typography'
import { Meta, StoryObj } from '@storybook/react'
import React from 'react'
import styled from 'styled-components'

import { Placeholder } from '@kl/icons/16'

import MetaData from './__meta__/meta.json'
import { Tabs, GroupTabs } from './Tabs'
import { TabsProps } from './types'
import { Tabs } from './Tabs'
import { badges } from '@sb/badges'
import { withMeta } from '@helpers/hocs/MetaComponent/withMeta'
import { sbHideControls } from '@helpers/storybookHelpers'
import { Placeholder } from '@kaspersky/icons/16'
import { Button, GroupTabs, H3, Space } from '..'
import styled from 'styled-components'

const meta: Meta<TabsProps> = {
title: 'Atoms/Tabs',
title: 'Hexa UI Components/Tabs',
component: Tabs,
argTypes: {
...sbHideControls(['onChange', 'onTabClick', 'style', 'destroyInactiveTabPane', 'theme', 'className', 'type'])
Expand All @@ -30,7 +38,7 @@ const meta: Meta<TabsProps> = {
klId: 'tabs-kl-id'
},
parameters: {
badges: [badges.reviewedByDesign],
badges: [badges.stable, badges.reviewedByDesign],
docs: {
page: withMeta(MetaData)
},
Expand All @@ -57,62 +65,142 @@ export const Basic: Story = {}
export const WithIconAndNumber: Story = {
render: (args: TabsProps) => (
<Tabs {...args}>
<Tabs.TabPane tab={<Tabs.TabPaneHead text="Tab" number={5} />} key="1">
<Tabs.TabPane
tab={<Tabs.TabPaneHead text="Tab" iconBefore={<Placeholder />} />}
key="1"
>
Content of Tab Pane 1
</Tabs.TabPane>
<Tabs.TabPane
tab={<Tabs.TabPaneHead text="Tab" icon={<Placeholder />} />}
tab={<Tabs.TabPaneHead text="Tab" iconAfter={<Placeholder />} />}
key="2"
>
Content of Tab Pane 2
</Tabs.TabPane>
<Tabs.TabPane
tab={
<Tabs.TabPaneHead text="Tab" icon={<Placeholder />} number={5} />
<Tabs.TabPaneHead text="Tab" iconBefore={<Placeholder />} iconAfter={<Placeholder />} />
}
key="3"
>
Content of Tab Pane 3
</Tabs.TabPane>
</Tabs>
)
}

export const WithIndicator: Story = {
render: (args: TabsProps) => (
<Tabs {...args}>
<Tabs.TabPane tab={<Tabs.TabPaneHead text="Tab" indicator />} key="1">
Content of Tab Pane 1
<Tabs.TabPane tab={<Tabs.TabPaneHead text="Tab" number={5} />} key="4">
Content of Tab Pane 4
</Tabs.TabPane>
<Tabs.TabPane
tab={<Tabs.TabPaneHead text="Tab" indicator number={5} />}
key="2"
tab={
<Tabs.TabPaneHead text="Tab" iconBefore={<Placeholder />} number={5} />
}
key="5"
>
Content of Tab Pane 2
Content of Tab Pane 5
</Tabs.TabPane>
<Tabs.TabPane
tab={<Tabs.TabPaneHead text="Tab" indicator icon={<Placeholder />} />}
key="3"
tab={
<Tabs.TabPaneHead text="Tab" iconAfter={<Placeholder />} number={5} />
}
key="6"
>
Content of Tab Pane 3
Content of Tab Pane 6
</Tabs.TabPane>
<Tabs.TabPane
tab={
<Tabs.TabPaneHead
text="Tab"
indicator
icon={<Placeholder />}
number={5}
/>
<Tabs.TabPaneHead text="Tab" iconBefore={<Placeholder />} iconAfter={<Placeholder />} number={5} />
}
key="4"
key="7"
>
Content of Tab Pane 4
Content of Tab Pane 7
</Tabs.TabPane>
</Tabs>
)
}

type StoryTabsProps = TabsProps & { indicatorMode: IndicatorMode }

export const WithIndicator: StoryObj<StoryTabsProps> = {
render: (args: StoryTabsProps) => {
const { indicatorMode, ...props } = args
return (
<Tabs {...props}>
<Tabs.TabPane
tab={<Tabs.TabPaneHead text="Tab" indicator indicatorMode={indicatorMode} />}
key="1"
>
Content of Tab Pane 1
</Tabs.TabPane>
<Tabs.TabPane
tab={<Tabs.TabPaneHead text="Tab" indicator indicatorMode={indicatorMode} number={5} />}
key="2"
>
Content of Tab Pane 2
</Tabs.TabPane>
<Tabs.TabPane
tab={
<Tabs.TabPaneHead
text="Tab"
indicator
indicatorMode={indicatorMode}
iconBefore={<Placeholder />}
/>}
key="3"
>
Content of Tab Pane 3
</Tabs.TabPane>
<Tabs.TabPane
tab={
<Tabs.TabPaneHead
text="Tab"
indicator
indicatorMode={indicatorMode}
iconAfter={<Placeholder />}
/>
}
key="4"
>
Content of Tab Pane 4
</Tabs.TabPane>
<Tabs.TabPane
tab={
<Tabs.TabPaneHead
text="Tab"
indicator
indicatorMode={indicatorMode}
iconBefore={<Placeholder />}
number={5}
/>
}
key="5"
>
Content of Tab Pane 5
</Tabs.TabPane>
<Tabs.TabPane
tab={
<Tabs.TabPaneHead
text="Tab"
indicator
indicatorMode={indicatorMode}
iconBefore={<Placeholder />}
iconAfter={<Placeholder />}
number={5}
/>
}
key="6"
>
Content of Tab Pane 6
</Tabs.TabPane>
</Tabs>
)
},
argTypes: {
indicatorMode: {
control: 'select',
options: IndicatorModes,
description: 'Indicator mode'
}
}
}

export const WithDisabled: Story = {
render: (args: TabsProps) => (
<Tabs {...args}>
Expand Down Expand Up @@ -148,24 +236,29 @@ export const WithDisabled: Story = {
)
}

const generateTabs = (length = 15, tabText = 'Tab', contentText = 'Content of tab') => Array.from({ length: length }, (_, i) => i).map((i) => ({
text: tabText,
disabled: i === 8,
content: contentText + ' ' + (i + 1)
}
))

export const CollapsedHorizontalGroup: Story = {
render: (props: TabsProps) => (
<Tabs {...props}>
{[
...Array.from({ length: 15 }, (_, i) => i)
].map((i) => (
<Tabs.TabPane
tab={
<Tabs.TabPaneHead text={`Tab ${i + 1}`} icon={<Placeholder />} />
}
key={i + 1}
disabled={i === 8}
>
Content of tab {i + 1}
</Tabs.TabPane>
))}
</Tabs>
)
render: (props: TabsProps) => {
return (
<Tabs {...props}>
{generateTabs(20, 'tabs.dropdown.more').map((el, i) => <Tabs.TabPane
tab={
<Tabs.TabPaneHead text={useLocalization(el.text) + ' ' + (i + 2)} icon={<Placeholder/>}/>
}
key={i + 1}
disabled={el.disabled}
>
{el.content}
</Tabs.TabPane>
)}
</Tabs>)
}
}

const RightButton = <Button mode='secondary' text='Right extra button'/>
Expand All @@ -174,7 +267,7 @@ const LeftButton = <Button mode='secondary' text='Left extra button'/>
export const WithExtraContent: Story = {
render: (args: TabsProps) => (
<Space size={16}>
<H3>Отступы и другие стили пока задавать надо вручную, в будущем будет проработано со стороны дизайна</H3>
<H3>Отѝтупы и другие ѝтили временно необходимо задавать вручную, в будущем будет проработано ѝо ѝтороны дизайна</H3>
<Tabs {...args} tabBarExtraContent={RightButton} />
<Tabs {...args} tabBarExtraContent={{ right: RightButton, left: LeftButton }} />
</Space>
Expand All @@ -184,7 +277,7 @@ export const WithExtraContent: Story = {
export const WithGroupedTabs: Story = {
render: (args: TabsProps) => (
<GroupTabs {...args}>
{/* Группа 1 */}
{/* Group 1 */}
<GroupTabs.TabPaneHeader
title={'Group 1 heading'}
key="tab-group-header-1"
Expand All @@ -209,7 +302,7 @@ export const WithGroupedTabs: Story = {
Content of Tab Pane 3
</Tabs.TabPane>

{/* Группа 2 */}
{/* Group 2 */}
<GroupTabs.TabPaneHeader
divider
title={'Group 2 heading'}
Expand All @@ -222,7 +315,7 @@ export const WithGroupedTabs: Story = {
Content of Tab Pane 5
</Tabs.TabPane>

{/* Группа 3 */}
{/* Group 3 */}
<GroupTabs.TabPaneHeader
divider
title={'Group 3 heading'}
Expand All @@ -237,7 +330,7 @@ export const WithGroupedTabs: Story = {
</GroupTabs>
),
args: {
defaultActiveKey: undefined
defaultActiveKey: '1'
},
argTypes: {
...sbHideControls(['tabPosition'])
Expand All @@ -261,3 +354,55 @@ export const TabsInsideTabs: Story = {
</Tabs>
)
}

const HighContainer = styled.div`
min-height: 80vh;
display: flex;
flex-direction: column;
`

const StretchedTabs = styled(Tabs)`
margin-top: 15px;
flex: 1 0 auto;
`

export const StretchedInHeight: Story = {
render: (args: TabsProps) => (
<HighContainer>
<H3>The Header</H3>
<StretchedTabs {...args} />
</HighContainer>
),
args: {
tabPosition: 'left'
}
}

export const WithTextbox: Story = {
render: (args: TabsProps) => (
<Space size={48}>
<Tabs {...args}>
<Tabs.TabPane tab={<Textbox />} key="1">
Content of Tab Pane 1
</Tabs.TabPane>
<Tabs.TabPane tab="Tab 2" key="2">
Content of Tab Pane 2
</Tabs.TabPane>
<Tabs.TabPane tab="Tab 3" key="3">
Content of Tab Pane 3
</Tabs.TabPane>
</Tabs>
<Tabs {...args}>
<Tabs.TabPane tab={<Textbox.Textarea />} key="1">
Content of Tab Pane 1
</Tabs.TabPane>
<Tabs.TabPane tab="Tab 2" key="2">
Content of Tab Pane 2
</Tabs.TabPane>
<Tabs.TabPane tab="Tab 3" key="3">
Content of Tab Pane 3
</Tabs.TabPane>
</Tabs>
</Space>
)
}
Loading

0 comments on commit 593734b

Please sign in to comment.