Skip to content
Draft
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
8 changes: 6 additions & 2 deletions packages/gamut/src/Popover/Popover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ export const Popover: React.FC<PopoverProps> = ({
variant,
}),
verticalOffset = getDefaultOffset({ axis: 'vertical', position, variant }),

widthRestricted,
zIndex,
}) => {
const [popoverHeight, setPopoverHeight] = useState<number>(0);
const [popoverWidth, setPopoverWidth] = useState<number>(0);
Expand Down Expand Up @@ -233,7 +233,11 @@ export const Popover: React.FC<PopoverProps> = ({
);

return (
<PopoverPortal animation={animation} isOpen={Boolean(isOpen && targetRef)}>
<PopoverPortal
animation={animation}
isOpen={Boolean(isOpen && targetRef)}
zIndex={zIndex}
>
{skipFocusTrap ? (
<>{contents}</>
) : (
Expand Down
8 changes: 4 additions & 4 deletions packages/gamut/src/Popover/elements.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,12 @@ export const PatternContainer = styled.div(
);

export const PopoverPortal: React.FC<
Pick<PopoverProps, 'animation' | 'isOpen'> & WithChildrenProp
> = ({ animation, isOpen, ...rest }) =>
Pick<PopoverProps, 'animation' | 'isOpen' | 'zIndex'> & WithChildrenProp
> = ({ animation, isOpen, zIndex, ...rest }) =>
animation ? (
<AnimatePresence>
{isOpen && (
<BodyPortal>
<BodyPortal zIndex={zIndex}>
<motion.div
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
Expand All @@ -81,7 +81,7 @@ export const PopoverPortal: React.FC<
)}
</AnimatePresence>
) : (
<BodyPortal {...rest} />
<BodyPortal zIndex={zIndex} {...rest} />
);

export type PopoverContainerProps = Pick<PopoverProps, 'position' | 'align'>;
Expand Down
5 changes: 5 additions & 0 deletions packages/gamut/src/Popover/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,4 +113,9 @@ export type PopoverProps = PopoverBaseProps &
* Whether to add width restrictions to Popover.
*/
widthRestricted?: boolean;

/**
* z-index for the Popover's portal. Defaults to 1.
*/
zIndex?: number;
};
2 changes: 2 additions & 0 deletions packages/gamut/src/Tip/shared/FloatingTip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export const FloatingTip: React.FC<TipWrapperProps> = ({
type,
username,
wrapperRef,
zIndex,
}) => {
const ref = useRef<HTMLDivElement>(null);

Expand Down Expand Up @@ -172,6 +173,7 @@ export const FloatingTip: React.FC<TipWrapperProps> = ({
targetRef={ref}
variant="secondary"
widthRestricted={false}
zIndex={zIndex}
>
<FloatingTipTextWrapper
horizNarrow={narrow && isHorizontalCenter}
Expand Down
7 changes: 4 additions & 3 deletions packages/gamut/src/Tip/shared/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,16 @@ export interface TipNewBaseProps {
* If ToolTipWrapper should inherit the dimensions of the parent element. Can only be used for inline tips.
*/
inheritDims?: boolean;
/**
* z-index for the tip's portal. Defaults to 1.
*/
zIndex?: number;
}
export interface TipInlineProps extends TipNewBaseProps {
placement?: 'inline';
zIndex?: number;
}
export interface TipFloatingProps extends TipNewBaseProps {
placement: 'floating';
zIndex?: never;
inheritDims?: never;
}

Expand All @@ -83,5 +85,4 @@ export type TipPlacementComponentProps = Omit<
| ((node: HTMLDivElement | null) => void);
type: 'info' | 'tool' | 'preview';
wrapperRef?: React.RefObject<HTMLDivElement>;
zIndex?: number;
} & React.PropsWithChildren;
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,12 @@ Using `popoverProps`, you can customize the look of the rendered popover. For ex

<Canvas of={CoachmarkStories.Customized} />

### Z-Index

You can customize the `zIndex` of the Coachmark's popover by passing `zIndex` through `popoverProps`. This is useful when the popover needs to appear above other positioned elements.

<Canvas of={CoachmarkStories.ZIndex} />

## Playground

<Canvas sourceState="shown" of={CoachmarkStories.Default} />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Coachmark, FillButton, FlexBox, Text } from '@codecademy/gamut';
import { Box, Coachmark, FillButton, FlexBox, Text } from '@codecademy/gamut';
import * as patterns from '@codecademy/gamut-patterns';
import type { Meta, StoryObj } from '@storybook/react';
import { ComponentProps, useEffect, useState } from 'react';
Expand Down Expand Up @@ -197,3 +197,39 @@
);
},
};

export const ZIndex: Story = {
args: {
popoverProps: {
position: 'below',
zIndex: 5,
},
},
render: function ZIndexExample(args) {
const [shouldShow, setShouldShow] = useState(true);

return (
<FlexBox flexDirection="column" gap={16}>
<Box bg="paleBlue" p={16} zIndex={3} position="relative">

Check warning on line 213 in packages/styleguide/src/lib/Molecules/Coachmark/Coachmark.stories.tsx

View workflow job for this annotation

GitHub Actions / lint (lint)

Props should be sorted alphabetically
Element with z-index: 3
</Box>
<Coachmark
{...args}
renderPopover={() => (
<FlexBox alignItems="flex-start" flexDirection="column" p={16}>
<Text mb={8}>This coachmark has z-index: 5 via popoverProps</Text>
<FillButton size="small" onClick={() => setShouldShow(false)}>
Got it
</FillButton>
</FlexBox>
)}
shouldShow={shouldShow}
>
<FillButton onClick={() => setShouldShow(true)}>
Show Coachmark
</FillButton>
</Coachmark>
</FlexBox>
);
},
};
10 changes: 10 additions & 0 deletions packages/styleguide/src/lib/Molecules/Modals/Modal/Modal.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,16 @@ A Modal can be made scrollable by including large content inside.

<Canvas of={ModalStories.Scrollable} />

## Z-Index

Modal accepts a `zIndex` prop (defaults to `3`) that controls the stacking order of its underlying Overlay. Use this when the modal needs to appear above other positioned elements like sticky headers or custom floating UI.

```tsx
<Modal zIndex={10} isOpen={isOpen} onRequestClose={handleClose}>
Content that needs to appear above other positioned elements
</Modal>
```

## Focus management

The `containerFocusRef` prop allows you to programmatically control focus on the Modal container. This is useful for advanced focus management scenarios where you need to override the default focus behavior (the Modal has `data-autofocus` by default).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ Unlike the legacy `Modal` implementations in the monolith, this:

- If you need styles such as a background behind content, see `Modal` for general modals and `Dialog` for confirmation flows.

## Z-Index

The `zIndex` prop controls the stacking order of the overlay. It defaults to `3`, which places it above most common UI elements. Increase this value when the overlay needs to appear above other positioned elements like sticky headers or floating UI.

<Canvas of={OverlayStories.ZIndex} />

## Playground

<Canvas sourceState="shown" of={OverlayStories.Default} />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { FillButton, FlexBox, Overlay, Text } from '@codecademy/gamut';
import type { Meta } from '@storybook/react';
import { Box, FillButton, FlexBox, Overlay, Text } from '@codecademy/gamut';
import type { Meta, StoryObj } from '@storybook/react';
import { useEffect, useState } from 'react';

const meta: Meta<typeof Overlay> = {
Expand All @@ -8,6 +8,7 @@
};

export default meta;
type Story = StoryObj<typeof Overlay>;

export const Default: React.FC<React.ComponentProps<typeof Overlay>> = (
args
Expand All @@ -34,3 +35,67 @@
</>
);
};

export const ZIndex: Story = {
render: function ZIndexExample() {
const [defaultOpen, setDefaultOpen] = useState(false);
const [customOpen, setCustomOpen] = useState(false);

return (
<FlexBox flexDirection="column" gap={16}>
<FlexBox gap={16}>
<FillButton onClick={() => setDefaultOpen(true)}>
Open Overlay (default z-index: 3)
</FillButton>
<FillButton onClick={() => setCustomOpen(true)}>
Open Overlay (z-index: 10)
</FillButton>
</FlexBox>

<Box position="relative" height="100px">

Check warning on line 55 in packages/styleguide/src/lib/Molecules/Modals/Overlay/Overlay.stories.tsx

View workflow job for this annotation

GitHub Actions / lint (lint)

Props should be sorted alphabetically
<Box
position="absolute"
top={0}
left={0}

Check warning on line 59 in packages/styleguide/src/lib/Molecules/Modals/Overlay/Overlay.stories.tsx

View workflow job for this annotation

GitHub Actions / lint (lint)

Props should be sorted alphabetically
bg="paleYellow"

Check warning on line 60 in packages/styleguide/src/lib/Molecules/Modals/Overlay/Overlay.stories.tsx

View workflow job for this annotation

GitHub Actions / lint (lint)

Props should be sorted alphabetically
p={16}

Check warning on line 61 in packages/styleguide/src/lib/Molecules/Modals/Overlay/Overlay.stories.tsx

View workflow job for this annotation

GitHub Actions / lint (lint)

Props should be sorted alphabetically
zIndex={5}
>
<Text>Element with z-index: 5</Text>
</Box>
</Box>

<Overlay
isOpen={defaultOpen}
onRequestClose={() => setDefaultOpen(false)}

Check warning on line 70 in packages/styleguide/src/lib/Molecules/Modals/Overlay/Overlay.stories.tsx

View workflow job for this annotation

GitHub Actions / lint (lint)

Callbacks must be listed after all other props
shroud
>
<FlexBox bg="white" p={24} borderRadius="md" flexDirection="column" gap={16}>

Check warning on line 73 in packages/styleguide/src/lib/Molecules/Modals/Overlay/Overlay.stories.tsx

View workflow job for this annotation

GitHub Actions / lint (lint)

Props should be sorted alphabetically

Check warning on line 73 in packages/styleguide/src/lib/Molecules/Modals/Overlay/Overlay.stories.tsx

View workflow job for this annotation

GitHub Actions / lint (lint)

Props should be sorted alphabetically

Check warning on line 73 in packages/styleguide/src/lib/Molecules/Modals/Overlay/Overlay.stories.tsx

View workflow job for this annotation

GitHub Actions / lint (lint)

Props should be sorted alphabetically
<Text>
This overlay uses the default z-index of 3.
<br />
It may appear behind elements with higher z-index values.
</Text>
<FillButton onClick={() => setDefaultOpen(false)}>Close</FillButton>
</FlexBox>
</Overlay>

<Overlay
isOpen={customOpen}
onRequestClose={() => setCustomOpen(false)}

Check warning on line 85 in packages/styleguide/src/lib/Molecules/Modals/Overlay/Overlay.stories.tsx

View workflow job for this annotation

GitHub Actions / lint (lint)

Callbacks must be listed after all other props
shroud
zIndex={10}
>
<FlexBox bg="white" p={24} borderRadius="md" flexDirection="column" gap={16}>
<Text>
This overlay uses z-index: 10.
<br />
It will appear above the yellow element with z-index: 5.
</Text>
<FillButton onClick={() => setCustomOpen(false)}>Close</FillButton>
</FlexBox>
</Overlay>
</FlexBox>
);
},
};
19 changes: 17 additions & 2 deletions packages/styleguide/src/lib/Molecules/Tips/InfoTip/InfoTip.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,24 @@ InfoTips have intelligent Escape key handling that works correctly both inside a

<Canvas of={InfoTipStories.InfoTipInsideModal} />

## InfoTips and zIndex
## Custom Accessible Labeling

You can change the zIndex of your `InfoTip` with the zIndex property.
Provide either `ariaLabel` or `ariaLabelledby` to ensure screen reader users understand the purpose of the InfoTip button.

The InfoTip button's accessible label can be customized using either prop:

- **`ariaLabel`**: Directly sets the accessible label text. Useful when you want to provide a custom label without referencing another element.
- **`ariaLabelledby`**: References the ID of another element to use as the label. Useful when you want the InfoTip button to be labeled by visible text elsewhere on the page. This is useful for when the `InfoTip` is beside text that contextualizes it.

### Custom Role Description

The `InfoTipButton` uses [`aria-roledescription="More information button"`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-roledescription) to provide additional context to screen reader users about the button's specific purpose.

<Canvas of={InfoTipStories.AriaLabel} />

## Z-Index

You can customize the `zIndex` of the InfoTip's portal with the `zIndex` prop. This works for both `inline` and `floating` placements, and is useful when the tip needs to appear above other positioned elements.

<Canvas of={InfoTipStories.ZIndex} />

Expand Down
Loading
Loading