From 3a9eed21ac6fba133f2bb84482e5649d2479511c Mon Sep 17 00:00:00 2001 From: Niels Roozemond Date: Thu, 8 Feb 2024 12:55:37 +0100 Subject: [PATCH] Switching tabs with context --- packages/css/src/components/tabs/tabs.scss | 40 +++++----- packages/react/src/Tabs/Tabs.test.tsx | 2 +- packages/react/src/Tabs/Tabs.tsx | 51 +++++++------ packages/react/src/Tabs/TabsButton.tsx | 44 +++++++++++ packages/react/src/Tabs/TabsContext.tsx | 13 ++++ packages/react/src/Tabs/TabsLink.tsx | 35 --------- packages/react/src/Tabs/TabsPanel.tsx | 25 +++++-- .../src/components/amsterdam/tabs.tokens.json | 4 +- .../storybook-react/src/Tabs/Tabs.stories.tsx | 73 +++++++++---------- 9 files changed, 160 insertions(+), 127 deletions(-) create mode 100644 packages/react/src/Tabs/TabsButton.tsx create mode 100644 packages/react/src/Tabs/TabsContext.tsx delete mode 100644 packages/react/src/Tabs/TabsLink.tsx diff --git a/packages/css/src/components/tabs/tabs.scss b/packages/css/src/components/tabs/tabs.scss index 6dbf38fba9..1ee524804f 100644 --- a/packages/css/src/components/tabs/tabs.scss +++ b/packages/css/src/components/tabs/tabs.scss @@ -30,42 +30,44 @@ } } -.amsterdam-tabs__link { - color: var(--amsterdam-tabs-link-color); +.amsterdam-tabs__button { + background-color: var(--amsterdam-tabs-button-background-color); + border: var(--amsterdam-tabs-button-border); + color: var(--amsterdam-tabs-button-color); cursor: pointer; display: inline-block; - font-family: var(--amsterdam-tabs-link-font-family); - font-size: var(--amsterdam-tabs-link-spacious-font-size); - font-weight: var(--amsterdam-tabs-link-font-weight); - line-height: var(--amsterdam-tabs-link-spacious-line-height); - outline-offset: var(--amsterdam-tabs-link-outline-offset); - padding-block: var(--amsterdam-tabs-link-padding-block); - padding-inline: var(--amsterdam-tabs-link-padding-inline); + font-family: var(--amsterdam-tabs-button-font-family); + font-size: var(--amsterdam-tabs-button-spacious-font-size); + font-weight: var(--amsterdam-tabs-button-font-weight); + line-height: var(--amsterdam-tabs-button-spacious-line-height); + outline-offset: var(--amsterdam-tabs-button-outline-offset); + padding-block: var(--amsterdam-tabs-button-padding-block); + padding-inline: var(--amsterdam-tabs-button-padding-inline); position: relative; text-decoration: none; - &:hover:not(.amsterdam-tabs__link--selected, .amsterdam-tabs__link--disabled)::after { + &:hover:not(.amsterdam-tabs__button--selected, .amsterdam-tabs__button--disabled)::after { background-color: currentColor; @extend .amsterdam-tabs-underline; } .amsterdam-theme--compact & { - font-size: var(--amsterdam-tabs-link-compact-font-size); - line-height: var(--amsterdam-tabs-link-compact-line-height); + font-size: var(--amsterdam-tabs-button-compact-font-size); + line-height: var(--amsterdam-tabs-button-compact-line-height); } - &:hover:not(.amsterdam-tabs__link--selected, .amsterdam-tabs__link--disabled) { - color: var(--amsterdam-tabs-link-hover-color); + &:hover:not(.amsterdam-tabs__button--selected, .amsterdam-tabs__button--disabled) { + color: var(--amsterdam-tabs-button-hover-color); } } -.amsterdam-tabs__link--selected { - background-color: var(--amsterdam-tabs-link-selected-background-color); - color: var(--amsterdam-tabs-link-selected-color); +.amsterdam-tabs__button--selected { + background-color: var(--amsterdam-tabs-button-selected-background-color); + color: var(--amsterdam-tabs-button-selected-color); } -.amsterdam-tabs__link--disabled { - color: var(--amsterdam-tabs-link-disabled-color); +.amsterdam-tabs__button--disabled { + color: var(--amsterdam-tabs-button-disabled-color); // TODO: Disabled cursor after button refactor // cursor: var(--amsterdam-action-disabled-cursor); diff --git a/packages/react/src/Tabs/Tabs.test.tsx b/packages/react/src/Tabs/Tabs.test.tsx index 5ad18551f5..3eadac7e9e 100644 --- a/packages/react/src/Tabs/Tabs.test.tsx +++ b/packages/react/src/Tabs/Tabs.test.tsx @@ -30,7 +30,7 @@ describe('Tabs', () => { }) it('supports ForwardRef in React', () => { - const ref = createRef() + const ref = createRef() const { container } = render() diff --git a/packages/react/src/Tabs/Tabs.tsx b/packages/react/src/Tabs/Tabs.tsx index c7944b49b8..3b07b5fcdf 100644 --- a/packages/react/src/Tabs/Tabs.tsx +++ b/packages/react/src/Tabs/Tabs.tsx @@ -4,9 +4,10 @@ */ import clsx from 'clsx' -import { forwardRef, useState, useTransition } from 'react' +import { createContext, forwardRef, useState } from 'react' import type { ForwardedRef, ForwardRefExoticComponent, HTMLAttributes, PropsWithChildren, RefAttributes } from 'react' -import { TabsLink } from './TabsLink' +import { TabsButton } from './TabsButton' +// import TabsContext from './TabsContext' import { TabsList } from './TabsList' import { TabsPanel } from './TabsPanel' @@ -14,44 +15,42 @@ export type TabsProps = {} & PropsWithChildren> type TabsComponent = { List: typeof TabsList - Link: typeof TabsLink + Button: typeof TabsButton Panel: typeof TabsPanel } & ForwardRefExoticComponent> -type TabsIndex = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 +// eslint-disable-next-line no-unused-vars +export const TabsContext = createContext({ activeTab: 0, updateTab: (_tab: number) => {} }) export const Tabs = forwardRef( ({ children, className, ...restProps }: TabsProps, ref: ForwardedRef) => { - const [selectedTab, setSelectedTab] = useState(0) - const [isPending, startTransition] = useTransition() - - const onTabClick = (tab: TabsIndex) => { - startTransition(() => { - setSelectedTab(tab) - }) + // const [activeTab] = useState(null) + // const [isPending, startTransition] = useTransition() + const [activeTab, setActiveTab] = useState(0) + const updateTab = (tab: number) => { + setActiveTab(tab) } return ( -
- - onTabClick(0)} selected={selectedTab === 0} /> - onTabClick(1)} selected={selectedTab === 1} /> - onTabClick(2)} selected={selectedTab === 2} /> + +
+ {/* + onTabClick(0)} selected={activeTab === 0} /> + onTabClick(1)} selected={activeTab === 1} /> + onTabClick(2)} selected={activeTab === 2} /> - {selectedTab === 0 && Gegevens} - {selectedTab === 1 && Aanslagen} - {selectedTab === 2 && Bezwaar} - {children} -
+ {activeTab === 0 && Gegevens} + {activeTab === 1 && Aanslagen} + {activeTab === 2 && Bezwaar} */} + + {children} +
+ ) }, ) as TabsComponent Tabs.List = TabsList -Tabs.Link = TabsLink +Tabs.Button = TabsButton Tabs.Panel = TabsPanel Tabs.displayName = 'Tabs' diff --git a/packages/react/src/Tabs/TabsButton.tsx b/packages/react/src/Tabs/TabsButton.tsx new file mode 100644 index 0000000000..e03f5f411a --- /dev/null +++ b/packages/react/src/Tabs/TabsButton.tsx @@ -0,0 +1,44 @@ +/** + * @license EUPL-1.2+ + * Copyright (c) 2023 Gemeente Amsterdam + */ + +import clsx from 'clsx' +import { forwardRef, startTransition, useContext } from 'react' +import type { ButtonHTMLAttributes, ForwardedRef, PropsWithChildren } from 'react' +import { TabsContext } from './Tabs' +// import TabsContext from './TabsContext' + +export type TabsButtonProps = { + label: string + tab: number + isDisabled?: boolean +} & PropsWithChildren> + +export const TabsButton = forwardRef( + ({ label, tab = 0, className, isDisabled, ...restProps }: TabsButtonProps, ref: ForwardedRef) => { + const { activeTab, updateTab } = useContext(TabsContext) + + return ( + + ) + }, +) + +TabsButton.displayName = 'Tabs.Button' diff --git a/packages/react/src/Tabs/TabsContext.tsx b/packages/react/src/Tabs/TabsContext.tsx new file mode 100644 index 0000000000..41da503c33 --- /dev/null +++ b/packages/react/src/Tabs/TabsContext.tsx @@ -0,0 +1,13 @@ +import { createContext } from 'react' + +export type TabsContextValue = { + activeTab: number +} + +const defaultValues: TabsContextValue = { + activeTab: 0, +} + +const TabsContext = createContext(defaultValues) + +export default TabsContext diff --git a/packages/react/src/Tabs/TabsLink.tsx b/packages/react/src/Tabs/TabsLink.tsx deleted file mode 100644 index 3c40a4a3d5..0000000000 --- a/packages/react/src/Tabs/TabsLink.tsx +++ /dev/null @@ -1,35 +0,0 @@ -/** - * @license EUPL-1.2+ - * Copyright (c) 2023 Gemeente Amsterdam - */ - -import clsx from 'clsx' -import { forwardRef } from 'react' -import type { AnchorHTMLAttributes, ForwardedRef, PropsWithChildren } from 'react' - -export type TabsLinkProps = { - label: string - selected?: boolean - isDisabled?: boolean -} & PropsWithChildren> - -export const TabsLink = forwardRef( - ({ label, className, selected, isDisabled, ...restProps }: TabsLinkProps, ref: ForwardedRef) => { - return ( - - {label} - - ) - }, -) - -TabsLink.displayName = 'Tabs.Link' diff --git a/packages/react/src/Tabs/TabsPanel.tsx b/packages/react/src/Tabs/TabsPanel.tsx index 9577d97666..8261ced2a9 100644 --- a/packages/react/src/Tabs/TabsPanel.tsx +++ b/packages/react/src/Tabs/TabsPanel.tsx @@ -4,17 +4,28 @@ */ import clsx from 'clsx' -import { forwardRef } from 'react' +import { forwardRef, useContext } from 'react' import type { ForwardedRef, HTMLAttributes, PropsWithChildren } from 'react' +import { TabsContext } from './Tabs' -export type TabsPanelProps = {} & PropsWithChildren> +export type TabsPanelProps = { + tab: number +} & PropsWithChildren> export const TabsPanel = forwardRef( - ({ children, className, ...restProps }: TabsPanelProps, ref: ForwardedRef) => ( -
- {children} -
- ), + ({ tab, children, className, ...restProps }: TabsPanelProps, ref: ForwardedRef) => { + const { activeTab } = useContext(TabsContext) + + if (tab !== activeTab) { + return null + } + + return ( +
+ {children} +
+ ) + }, ) TabsPanel.displayName = 'Tabs.Panel' diff --git a/proprietary/tokens/src/components/amsterdam/tabs.tokens.json b/proprietary/tokens/src/components/amsterdam/tabs.tokens.json index 432f566337..bc1e0d28a5 100644 --- a/proprietary/tokens/src/components/amsterdam/tabs.tokens.json +++ b/proprietary/tokens/src/components/amsterdam/tabs.tokens.json @@ -6,7 +6,9 @@ "list": { "border-color": { "value": "{amsterdam.color.primary-blue}" } }, - "link": { + "button": { + "background-color": { "value": "transparent" }, + "border": { "value": "none" }, "color": { "value": "{amsterdam.link-appearance.color}" }, "font-family": { "value": "{amsterdam.typography.font-family}" }, "font-weight": { "value": "{amsterdam.typography.font-weight.normal}" }, diff --git a/storybook/storybook-react/src/Tabs/Tabs.stories.tsx b/storybook/storybook-react/src/Tabs/Tabs.stories.tsx index 7e89035a9a..193339efa0 100644 --- a/storybook/storybook-react/src/Tabs/Tabs.stories.tsx +++ b/storybook/storybook-react/src/Tabs/Tabs.stories.tsx @@ -3,9 +3,9 @@ * Copyright (c) 2023 Gemeente Amsterdam */ -import { Tabs } from '@amsterdam/design-system-react' +import { Heading, Paragraph, Tabs } from '@amsterdam/design-system-react' import { Meta, StoryObj } from '@storybook/react' -// import { exampleParagraph } from '../shared/exampleContent' +import { exampleParagraph } from '../shared/exampleContent' const meta = { title: 'Navigation/Tabs', @@ -20,20 +20,16 @@ const meta = { export default meta const tabMeta = { - component: Tabs.Link, + component: Tabs.Button, args: { label: 'Gegevens', - href: '#', }, argTypes: { label: { control: { type: 'text' }, }, - href: { - control: { type: 'text' }, - }, - selected: { - control: { type: 'boolean' }, + key: { + control: { type: 'number', min: 0, max: 9 }, }, isDisabled: { control: { type: 'boolean' }, @@ -46,48 +42,49 @@ const tabMeta = {
), ], -} satisfies Meta +} satisfies Meta type Story = StoryObj type TabStory = StoryObj const StoryTemplate: Story = { args: { - // children: [ - // <> - // - // - // - // - // - // - // - // Gegevens - // {exampleParagraph()} - // - // - // Aanslagen - // {exampleParagraph()} - // - // - // Documenten - // {exampleParagraph()} - // - // - // Acties - // {exampleParagraph()} - // - // , - // ], + children: [ + <> + + + + + + + + Gegevens + {exampleParagraph()} + + + Aanslagen + {exampleParagraph()} + + + Documenten + {exampleParagraph()} + + + Acties + {exampleParagraph()} + + , + ], }, } const TabStoryTemplate: TabStory = { args: { label: 'Gegevens', - href: '#', + tab: 1, + isDisabled: false, }, - render: ({ ...args }) => , + render: ({ ...args }) => , } export const Default: Story = {