Skip to content

Commit

Permalink
SideNavigation, Tabs, DefaultLabelProvider: implemented Indicator (#3901
Browse files Browse the repository at this point in the history
)
  • Loading branch information
AlbertCarreras authored Dec 2, 2024
1 parent 6d7703d commit 604071c
Show file tree
Hide file tree
Showing 14 changed files with 123 additions and 131 deletions.
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.

22 changes: 15 additions & 7 deletions packages/gestalt/src/__snapshots__/Indicator.jsdom.test.tsx.snap
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

0 comments on commit 604071c

Please sign in to comment.