Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SideNavigation, Tabs, DefaultLabelProvider: implemented Indicator #3901

Merged
merged 4 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/examples/defaultlabelprovider/translations.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@ const labels = {
TableOfContents: {
accessibilityLabel: myI18nTranslator('Table of contents'),
},
Tabs: {
accessibilityNotificationLabel: myI18nTranslator(
'This tab is displaying a notification indicator',
),
},
Tag: {
accessibilityErrorIconLabel: myI18nTranslator('Error'),
accessibilityRemoveIconLabel: myI18nTranslator('Remove tag'),
Expand Down
5 changes: 5 additions & 0 deletions docs/pages/web/utilities/defaultlabelprovider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,11 @@ function getLabelsTable(/* fallbackLabels: { [string]: { [string]: mixed } } */)
prop: 'tooltipMessage',
label: 'Click to learn more',
},
{
component: 'Tabs',
prop: 'indicator',
label: 'This tab is displaying a notification indicator',
},
{
component: 'Toast',
prop: 'accessibilityDismissButtonLabel',
Expand Down
4 changes: 1 addition & 3 deletions packages/gestalt/src/Badge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ import useInExperiment from './useInExperiment';
import useInteractiveStates from './utils/useInteractiveStates';
import { Indexable } from './zIndex';

type Position = 'middle' | 'top';

type TooltipProps = {
accessibilityLabel?: string;
idealDirection?: 'up' | 'right' | 'down' | 'left';
Expand Down Expand Up @@ -50,7 +48,7 @@ type Props = {
/**
* Badge position relative to its parent element. See the [positioning](https://gestalt.pinterest.systems/web/badge#Positioning) variant to learn more.
*/
position?: Position;
position?: 'middle' | 'top';
/**
* Text displayed inside of the Badge. Sentence case is preferred.
*/
Expand Down
10 changes: 10 additions & 0 deletions packages/gestalt/src/Indicator.css
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,13 @@
.marginTop {
padding-top: 1px;
}

.placementMiddle {
display: inline-block;
vertical-align: middle;
}

.placementTop {
display: inline-block;
vertical-align: top;
}
26 changes: 20 additions & 6 deletions packages/gestalt/src/Indicator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ type Props = {
* Available for testing purposes, if needed. Consider [better queries](https://testing-library.com/docs/queries/about/#priority) before using this prop.
*/
dataTestId?: string;
/**
* Indicator position relative to its parent element. See the [positioning](https://gestalt.pinterest.systems/web/indicator#Positioning) variant to learn more.
*/
position?: 'middle' | 'top';
};

/**
Expand All @@ -25,7 +29,12 @@ type Props = {
* ![Indicator dark mode](https://raw.githubusercontent.com/pinterest/gestalt/master/playwright/visual-test/Indicator-dark.spec.ts-snapshots/Indicator-dark-chromium-darwin.png)
*
*/
export default function Indicator({ accessibilityLabel, count, dataTestId }: Props) {
export default function Indicator({
accessibilityLabel,
count,
dataTestId,
position = 'middle',
}: Props) {
const isInVRExperiment = useInExperiment({
webExperimentName: 'web_gestalt_visualRefresh',
mwebExperimentName: 'web_gestalt_visualRefresh',
Expand All @@ -35,7 +44,10 @@ export default function Indicator({ accessibilityLabel, count, dataTestId }: Pro
return (
<div
aria-label={accessibilityLabel}
className={classnames(styles.notification)}
className={classnames(
styles.notification,
position === 'middle' ? styles.placementMiddle : styles.placementTop,
)}
data-test-id={dataTestId}
role="status"
/>
Expand All @@ -47,13 +59,15 @@ export default function Indicator({ accessibilityLabel, count, dataTestId }: Pro
return (
<div
aria-label={accessibilityLabel}
className={classnames(styles.counter, { [styles.marginTop]: !isInVRExperiment })}
className={classnames(position === 'middle' ? styles.placementMiddle : styles.placementTop)}
data-test-id={dataTestId}
role="status"
>
<TextUI align="center" color="light" size="xs">
{displayCount}
</TextUI>
<div className={classnames(styles.counter, { [styles.marginTop]: !isInVRExperiment })}>
<TextUI align="center" color="light" size="xs">
{displayCount}
</TextUI>
</div>
</div>
);
}
23 changes: 5 additions & 18 deletions packages/gestalt/src/SideNavigation/ItemContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { useSideNavigation } from '../contexts/SideNavigationProvider';
import Flex from '../Flex';
import Icon from '../Icon';
import icons from '../icons/index';
import Indicator from '../Indicator';
import Text from '../Text';
import { Indexable } from '../zIndex';

Expand Down Expand Up @@ -145,16 +146,9 @@ export default function ItemContent({
width={collapsed ? 44 : undefined}
>
{collapsed && icon && notificationAccessibilityLabel ? (
<Box
aria-label={notificationAccessibilityLabel}
color="primary"
dangerouslySetInlineStyle={{ __style: { top: 4, right: 4 } }}
height={8}
position="absolute"
role="status"
rounding="circle"
width={8}
/>
<Box dangerouslySetInlineStyle={{ __style: { right: 4, top: 0 } }} position="absolute">
<Indicator accessibilityLabel={notificationAccessibilityLabel} position="top" />
</Box>
) : null}

<Flex
Expand Down Expand Up @@ -200,14 +194,7 @@ export default function ItemContent({
<Badge text={badge.text} type={badge.type} />
) : null}
{notificationAccessibilityLabel ? (
<Box
aria-label={notificationAccessibilityLabel}
color="primary"
height={8}
role="status"
rounding="circle"
width={8}
/>
<Indicator accessibilityLabel={notificationAccessibilityLabel} />
) : null}
</Box>
)}
Expand Down
8 changes: 3 additions & 5 deletions packages/gestalt/src/Tabs.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ReactNode } from 'react';
import { LegacyRef, ReactNode } from 'react';
import Flex from './Flex';
import Tab from './Tabs/Tab';
import useInExperiment from './useInExperiment';
Expand Down Expand Up @@ -28,12 +28,11 @@ type Props = {
* The array of tabs to be displayed. The active tab (as indicated by `activeTabIndex`) will be underlined. Use the optional `indicator` field to show a notification of new items on the tab — see the [indicator variant](https://gestalt.pinterest.systems/web/tabs#Indicator) to learn more. Though `text` currently accepts a React.Node, this is deprecated and will be replaced by a simple `string` type soon.
*/
tabs: ReadonlyArray<{
notificationAccessibilityLabel?: string;
href: string;
id?: string;
indicator?: 'dot' | number;
ref?: {
current: HTMLElement | null | undefined;
};
ref?: LegacyRef<HTMLDivElement> | undefined;
text: ReactNode;
}>;
/**
Expand Down Expand Up @@ -72,7 +71,6 @@ export default function Tabs({
{tabs.map(({ href, id, indicator, ref, text }, index) => (
<Tab
key={id || `${href}_${index}`}
// @ts-expect-error - TS2322 - Type '{ current: HTMLElement | null | undefined; } | undefined' is not assignable to type 'LegacyRef<HTMLElement> | undefined'.
ref={ref}
bgColor={bgColor}
dataTestId={dataTestId}
Expand Down
47 changes: 42 additions & 5 deletions packages/gestalt/src/Tabs/Tab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ import {
TOKEN_COLOR_BACKGROUND_TABS_TRANSPARENT_ACTIVE,
TOKEN_COLOR_BACKGROUND_TABS_TRANSPARENT_BASE,
TOKEN_COLOR_BACKGROUND_TABS_TRANSPARENT_HOVER,
TOKEN_ROUNDING_0,
} from 'gestalt-design-tokens';
import { Count, Notification, Underline } from './subcomponents';
import Box from '../Box';
import { useDefaultLabelContext } from '../contexts/DefaultLabelProvider';
import Flex from '../Flex';
import Indicator from '../Indicator';
import style from '../Tabs.css';
import TapAreaLink from '../TapAreaLink';
import TextUI from '../TextUI';
Expand All @@ -19,6 +21,7 @@ import useInExperiment from '../useInExperiment';
import useInteractiveStates from '../utils/useInteractiveStates';

type TabType = {
notificationAccessibilityLabel?: string;
href: string;
id?: string;
indicator?: 'dot' | number;
Expand Down Expand Up @@ -51,7 +54,18 @@ const COLORS = Object.freeze({
});

const TabWithForwardRef = forwardRef<HTMLDivElement, TabProps>(function Tab(
{ bgColor, href, indicator, id, index, isActive, onChange, text, dataTestId }: TabProps,
{
notificationAccessibilityLabel,
bgColor,
href,
indicator,
id,
index,
isActive,
onChange,
text,
dataTestId,
}: TabProps,
ref,
) {
const {
Expand All @@ -73,6 +87,8 @@ const TabWithForwardRef = forwardRef<HTMLDivElement, TabProps>(function Tab(
mwebExperimentName: 'web_gestalt_visualRefresh',
});

const { accessibilityNotificationLabel } = useDefaultLabelContext('Tabs');

const isRtl = typeof document === 'undefined' ? false : document?.dir === 'rtl';

let color = COLORS[bgColor].base;
Expand Down Expand Up @@ -135,10 +151,21 @@ const TabWithForwardRef = forwardRef<HTMLDivElement, TabProps>(function Tab(
{text}
</TextUI>

{indicator === 'dot' && <Notification />}
{indicator === 'dot' && (
<Indicator
accessibilityLabel={
notificationAccessibilityLabel ?? accessibilityNotificationLabel
}
/>
)}
{/* Number.isFinite will return false for a string or undefined */}
{typeof indicator === 'number' && Number.isFinite(indicator) && (
<Count count={indicator} />
<Indicator
accessibilityLabel={
notificationAccessibilityLabel ?? accessibilityNotificationLabel
}
count={indicator}
/>
)}
</Flex>

Expand All @@ -155,7 +182,17 @@ const TabWithForwardRef = forwardRef<HTMLDivElement, TabProps>(function Tab(
// 4px/boint, padding on left and right
width={`calc(100% - ${(isInVRExperiment ? 2 : 2) * 4 * 2}px)`}
>
<Underline />
{/* Active tab underline */}
<Box
color="selected"
dangerouslySetInlineStyle={{
__style: {
borderRadius: isInVRExperiment ? TOKEN_ROUNDING_0 : 1.5,
},
}}
height={isInVRExperiment ? 2 : 3}
width="100%"
/>
</Box>
)}
</Box>
Expand Down
64 changes: 0 additions & 64 deletions packages/gestalt/src/Tabs/subcomponents.tsx

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@ exports[`Indicator as counter with 1 digit 1`] = `
<div>
<div
aria-label="Counter"
class="counter marginTop"
class="placementMiddle"
role="status"
>
<div
class="light alignCenter breakWord Text fontWeightSemiBold fontSize100"
class="counter marginTop"
>
3
<div
class="light alignCenter breakWord Text fontWeightSemiBold fontSize100"
>
3
</div>
</div>
</div>
</div>
Expand All @@ -23,13 +27,17 @@ exports[`Indicator as counter with 3 digits 1`] = `
<div>
<div
aria-label="Counter"
class="counter marginTop"
class="placementMiddle"
role="status"
>
<div
class="light alignCenter breakWord Text fontWeightSemiBold fontSize100"
class="counter marginTop"
>
99+
<div
class="light alignCenter breakWord Text fontWeightSemiBold fontSize100"
>
99+
</div>
</div>
</div>
</div>
Expand All @@ -41,7 +49,7 @@ exports[`Indicator as notification 1`] = `
<div>
<div
aria-label="Notification"
class="notification"
class="notification placementMiddle"
role="status"
/>
</div>
Expand Down
Loading
Loading