Improve documentation
- Fix tests Signed-off-by: Philipp Hugenroth <philipph@spotify.com>
This commit is contained in:
@@ -700,11 +700,20 @@ export function MissingAnnotationEmptyState(props: Props_3): JSX.Element;
|
||||
// @public (undocumented)
|
||||
export type MissingAnnotationEmptyStateClassKey = 'code';
|
||||
|
||||
// @public (undocumented)
|
||||
// @public
|
||||
export const MobileSidebar: (
|
||||
props: React_2.PropsWithChildren<{}>,
|
||||
) => JSX.Element | null;
|
||||
|
||||
// @public
|
||||
export const MobileSidebarContext: React_2.Context<MobileSidebarContextType>;
|
||||
|
||||
// @public
|
||||
export type MobileSidebarContextType = {
|
||||
selectedMenuItemIndex: number;
|
||||
setSelectedMenuItemIndex: React_2.Dispatch<React_2.SetStateAction<number>>;
|
||||
};
|
||||
|
||||
// Warning: (ae-missing-release-tag) "OAuthRequestDialog" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
@@ -844,7 +853,7 @@ export type SelectItem = {
|
||||
value: string | number;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
// @public
|
||||
export const Sidebar: (
|
||||
props: React_2.PropsWithChildren<SidebarProps>,
|
||||
) => JSX.Element;
|
||||
@@ -879,12 +888,12 @@ export const sidebarConfig: {
|
||||
|
||||
// Warning: (ae-missing-release-tag) "SidebarContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
// @public
|
||||
export const SidebarContext: Context<SidebarContextType>;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "SidebarContextType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
// @public
|
||||
export type SidebarContextType = {
|
||||
isOpen: boolean;
|
||||
setOpen?: (open: boolean) => void;
|
||||
@@ -1167,16 +1176,14 @@ export type SidebarDividerClassKey = 'root';
|
||||
// @public
|
||||
export const SidebarExpandButton: () => JSX.Element | null;
|
||||
|
||||
// @public (undocumented)
|
||||
// @public
|
||||
export const SidebarGroup: (
|
||||
props: React_2.PropsWithChildren<SidebarGroupProps>,
|
||||
) => JSX.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
// @public
|
||||
export interface SidebarGroupProps extends BottomNavigationActionProps {
|
||||
// (undocumented)
|
||||
priority?: number;
|
||||
// (undocumented)
|
||||
to?: string;
|
||||
}
|
||||
|
||||
@@ -1196,19 +1203,19 @@ export type SidebarIntroClassKey =
|
||||
// Warning: (ae-forgotten-export) The symbol "SidebarItemProps" needs to be exported by the entry point index.d.ts
|
||||
// Warning: (ae-missing-release-tag) "SidebarItem" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
// @public
|
||||
export const SidebarItem: React_2.ForwardRefExoticComponent<
|
||||
SidebarItemProps & React_2.RefAttributes<any>
|
||||
>;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "SidebarItemClassKey" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export type SidebarItemClassKey =
|
||||
| 'root'
|
||||
| 'buttonItem'
|
||||
| 'closed'
|
||||
| 'open'
|
||||
| 'highlightable'
|
||||
| 'highlighted'
|
||||
| 'label'
|
||||
| 'iconContainer'
|
||||
| 'searchRoot'
|
||||
@@ -1216,6 +1223,10 @@ export type SidebarItemClassKey =
|
||||
| 'searchFieldHTMLInput'
|
||||
| 'searchContainer'
|
||||
| 'secondaryAction'
|
||||
| 'closedItemIcon'
|
||||
| 'submenuArrow'
|
||||
| 'expandButton'
|
||||
| 'arrows'
|
||||
| 'selected';
|
||||
|
||||
// Warning: (ae-missing-release-tag) "SidebarPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
@@ -2058,10 +2069,10 @@ export const SidebarSpacer: React_2.ComponentType<
|
||||
// @public (undocumented)
|
||||
export type SidebarSpacerClassKey = 'root';
|
||||
|
||||
// @public (undocumented)
|
||||
// @public
|
||||
export const SidebarStateContext: React_2.Context<SidebarStateContextType>;
|
||||
|
||||
// @public (undocumented)
|
||||
// @public
|
||||
export type SidebarStateContextType = {
|
||||
isPinned: boolean;
|
||||
toggleSidebarPinState: () => any;
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { PropsWithChildren } from 'react';
|
||||
import { BackstageTheme } from '@backstage/theme';
|
||||
import { makeStyles, ThemeProvider } from '@material-ui/core/styles';
|
||||
import React, { PropsWithChildren } from 'react';
|
||||
|
||||
export type PageClassKey = 'root';
|
||||
|
||||
|
||||
@@ -14,19 +14,23 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import AcUnitIcon from '@material-ui/icons/AcUnit';
|
||||
import CreateComponentIcon from '@material-ui/icons/AddCircleOutline';
|
||||
import BuildRoundedIcon from '@material-ui/icons/BuildRounded';
|
||||
import MenuBookIcon from '@material-ui/icons/MenuBook';
|
||||
import { screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import BuildRoundedIcon from '@material-ui/icons/BuildRounded';
|
||||
import CreateComponentIcon from '@material-ui/icons/AddCircleOutline';
|
||||
import MenuBookIcon from '@material-ui/icons/MenuBook';
|
||||
import AcUnitIcon from '@material-ui/icons/AcUnit';
|
||||
import { Sidebar } from './Bar';
|
||||
import { SidebarItem, SidebarSearchField, SidebarExpandButton } from './Items';
|
||||
import { SidebarSubmenuItem } from './SidebarSubmenuItem';
|
||||
import { SidebarSubmenu } from './SidebarSubmenu';
|
||||
import { SidebarStateContext } from '.';
|
||||
import React from 'react';
|
||||
import {
|
||||
Sidebar,
|
||||
SidebarExpandButton,
|
||||
SidebarItem,
|
||||
SidebarSearchField,
|
||||
SidebarStateContext,
|
||||
SidebarSubmenu,
|
||||
SidebarSubmenuItem,
|
||||
} from '.';
|
||||
|
||||
async function renderScalableSidebar() {
|
||||
await renderInTestApp(
|
||||
@@ -37,7 +41,7 @@ async function renderScalableSidebar() {
|
||||
toggleSidebarPinState: () => {},
|
||||
}}
|
||||
>
|
||||
<Sidebar>
|
||||
<Sidebar disableExpandOnHover>
|
||||
<SidebarSearchField onSearch={() => {}} to="/search" />
|
||||
<SidebarItem icon={MenuBookIcon} onClick={() => {}} text="Catalog">
|
||||
<SidebarSubmenu title="Catalog">
|
||||
|
||||
@@ -36,7 +36,7 @@ const useStyles = makeStyles<BackstageTheme>(
|
||||
left: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
zIndex: 1000,
|
||||
zIndex: theme.zIndex.appBar,
|
||||
background: theme.palette.navigation.background,
|
||||
overflowX: 'hidden',
|
||||
msOverflowStyle: 'none',
|
||||
@@ -77,12 +77,23 @@ export type SidebarProps = {
|
||||
disableExpandOnHover?: boolean;
|
||||
};
|
||||
|
||||
const DesktopSidebar = ({
|
||||
openDelayMs = sidebarConfig.defaultOpenDelayMs,
|
||||
closeDelayMs = sidebarConfig.defaultCloseDelayMs,
|
||||
disableExpandOnHover,
|
||||
children,
|
||||
}: PropsWithChildren<SidebarProps>) => {
|
||||
/**
|
||||
* Places the Sidebar & wraps the children providing context weather the `Sidebar` is open or not.
|
||||
*
|
||||
* Handles & delays hover events for expanding the `Sidebar`
|
||||
*
|
||||
* @param props `disableExpandOnHover` disables the default hover behaviour;
|
||||
* `openDelayMs` & `closeDelayMs` set delay until sidebar will open/close on hover
|
||||
* @returns
|
||||
* @internal
|
||||
*/
|
||||
const DesktopSidebar = (props: PropsWithChildren<SidebarProps>) => {
|
||||
const {
|
||||
openDelayMs = sidebarConfig.defaultOpenDelayMs,
|
||||
closeDelayMs = sidebarConfig.defaultCloseDelayMs,
|
||||
disableExpandOnHover,
|
||||
children,
|
||||
} = props;
|
||||
const classes = useStyles();
|
||||
const isSmallScreen = useMediaQuery<BackstageTheme>(
|
||||
theme => theme.breakpoints.down('md'),
|
||||
@@ -130,6 +141,9 @@ const DesktopSidebar = ({
|
||||
|
||||
const isOpen = (state === State.Open && !isSmallScreen) || isPinned;
|
||||
|
||||
/**
|
||||
* Close/Open Sidebar directily without delays. Also toggles `SidebarPinState` to avoid hidden content behind Sidebar.
|
||||
*/
|
||||
const setOpen = (open: boolean) => {
|
||||
if (open) {
|
||||
setState(State.Open);
|
||||
@@ -163,7 +177,11 @@ const DesktopSidebar = ({
|
||||
);
|
||||
};
|
||||
|
||||
/** @public */
|
||||
/**
|
||||
* Passing children into the desktop or mobile sidebar depending on the context
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const Sidebar = (props: React.PropsWithChildren<SidebarProps>) => {
|
||||
const { children, openDelayMs, closeDelayMs, disableExpandOnHover } = props;
|
||||
const { isMobile } = useContext(SidebarStateContext);
|
||||
|
||||
@@ -37,9 +37,9 @@ import React, {
|
||||
import {
|
||||
Link,
|
||||
NavLinkProps,
|
||||
resolvePath,
|
||||
useLocation,
|
||||
useResolvedPath,
|
||||
resolvePath,
|
||||
} from 'react-router-dom';
|
||||
import {
|
||||
sidebarConfig,
|
||||
@@ -56,11 +56,14 @@ import DoubleArrowRight from './icons/DoubleArrowRight';
|
||||
import { isLocationMatch } from './utils';
|
||||
import { Location } from 'history';
|
||||
|
||||
/** @public */
|
||||
export type SidebarItemClassKey =
|
||||
| 'root'
|
||||
| 'buttonItem'
|
||||
| 'closed'
|
||||
| 'open'
|
||||
| 'highlightable'
|
||||
| 'highlighted'
|
||||
| 'label'
|
||||
| 'iconContainer'
|
||||
| 'searchRoot'
|
||||
@@ -68,6 +71,10 @@ export type SidebarItemClassKey =
|
||||
| 'searchFieldHTMLInput'
|
||||
| 'searchContainer'
|
||||
| 'secondaryAction'
|
||||
| 'closedItemIcon'
|
||||
| 'submenuArrow'
|
||||
| 'expandButton'
|
||||
| 'arrows'
|
||||
| 'selected';
|
||||
|
||||
const useStyles = makeStyles<BackstageTheme>(
|
||||
@@ -238,12 +245,18 @@ const useStyles = makeStyles<BackstageTheme>(
|
||||
{ name: 'BackstageSidebarItem' },
|
||||
);
|
||||
|
||||
/**
|
||||
* Evaluates the routes of the SubmenuItems & nested DropdownItems.
|
||||
* The reeveluation is only triggered, if the `locationPathname` changes, as `useElementFilter` uses memorization.
|
||||
*
|
||||
* @param submenu SidebarSubmenu component
|
||||
* @param location Location
|
||||
* @returns boolean
|
||||
*/
|
||||
const useLocationMatch = (
|
||||
submenu: React.ReactElement<SidebarSubmenuProps>,
|
||||
location: Location,
|
||||
): boolean =>
|
||||
// Evaluates the routes of the SubmenuItems & nested DropdownItems.
|
||||
// The reeveluation is only triggered, if the `locationPathname` changes, as `useElementFilter` uses memorization
|
||||
useElementFilter(
|
||||
submenu.props.children,
|
||||
elements => {
|
||||
@@ -367,6 +380,9 @@ export const WorkaroundNavLink = React.forwardRef<
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* Common component used by SidebarItem & SidebarItemWithSubmenu
|
||||
*/
|
||||
const SidebarItemBase = forwardRef<any, SidebarItemProps>((props, ref) => {
|
||||
const {
|
||||
icon: Icon,
|
||||
@@ -507,6 +523,11 @@ const SidebarItemWithSubmenu = ({
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a `SidebarItem`
|
||||
*
|
||||
* If children contain a `SidebarSubmenu` component the `SidebarItem` will have a expandable submenu
|
||||
*/
|
||||
export const SidebarItem = forwardRef<any, SidebarItemProps>((props, ref) => {
|
||||
// Filter children for SidebarSubmenu components
|
||||
const [submenu] = useElementFilter(props.children, elements =>
|
||||
@@ -655,6 +676,8 @@ export const SidebarScrollWrapper = styled('div')(({ theme }) => {
|
||||
* A button which allows you to expand the sidebar when clicked.
|
||||
* Use optionally to replace sidebar's expand-on-hover feature with expand-on-click.
|
||||
*
|
||||
* If you are using this you might want to set the `disableExpandOnHover` of the `Sidebar` to `true`.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const SidebarExpandButton = () => {
|
||||
|
||||
@@ -21,32 +21,39 @@ import LayersIcon from '@material-ui/icons/Layers';
|
||||
import LibraryBooks from '@material-ui/icons/LibraryBooks';
|
||||
import { fireEvent } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { Sidebar } from './Bar';
|
||||
import { SidebarItem } from './Items';
|
||||
import { MobileSidebar } from './MobileSidebar';
|
||||
import { SidebarGroup } from './SidebarGroup';
|
||||
import {
|
||||
MobileSidebar,
|
||||
Sidebar,
|
||||
SidebarGroup,
|
||||
SidebarItem,
|
||||
SidebarPage,
|
||||
} from '.';
|
||||
|
||||
const MobileSidebarWithGroups = () => (
|
||||
<MobileSidebar>
|
||||
<h1>Header</h1>
|
||||
<SidebarGroup icon={<HomeIcon />} label="Menu">
|
||||
<SidebarItem icon={HomeIcon} to="/" text="Home" />
|
||||
<SidebarItem icon={LayersIcon} to="/" text="Explore" />
|
||||
<SidebarItem icon={LibraryBooks} to="/" text="Docs" />
|
||||
</SidebarGroup>
|
||||
<div>Content</div>
|
||||
<div>More Content</div>
|
||||
<SidebarGroup icon={<CreateComponentIcon />} label="Create" to="#" />
|
||||
<footer>Footer</footer>
|
||||
</MobileSidebar>
|
||||
<SidebarPage>
|
||||
<MobileSidebar>
|
||||
<h1>Header</h1>
|
||||
<SidebarGroup icon={<HomeIcon />} label="Menu">
|
||||
<SidebarItem icon={HomeIcon} to="/" text="Home" />
|
||||
<SidebarItem icon={LayersIcon} to="/" text="Explore" />
|
||||
<SidebarItem icon={LibraryBooks} to="/" text="Docs" />
|
||||
</SidebarGroup>
|
||||
<div>Content</div>
|
||||
<div>More Content</div>
|
||||
<SidebarGroup icon={<CreateComponentIcon />} label="Create" to="#" />
|
||||
<footer>Footer</footer>
|
||||
</MobileSidebar>
|
||||
</SidebarPage>
|
||||
);
|
||||
|
||||
const MobileSidebarWithoutGroups = () => (
|
||||
<MobileSidebar>
|
||||
<SidebarItem icon={HomeIcon} to="/one" text="Home" />
|
||||
<SidebarItem icon={LayersIcon} to="/two" text="Explore" />
|
||||
<SidebarItem icon={LibraryBooks} to="/three" text="Docs" />
|
||||
</MobileSidebar>
|
||||
<SidebarPage>
|
||||
<MobileSidebar>
|
||||
<SidebarItem icon={HomeIcon} to="/one" text="Home" />
|
||||
<SidebarItem icon={LayersIcon} to="/two" text="Explore" />
|
||||
<SidebarItem icon={LibraryBooks} to="/three" text="Docs" />
|
||||
</MobileSidebar>
|
||||
</SidebarPage>
|
||||
);
|
||||
|
||||
describe('<MobileSidebar />', () => {
|
||||
@@ -56,9 +63,11 @@ describe('<MobileSidebar />', () => {
|
||||
|
||||
it('should render MobileSidebar on smaller screens', async () => {
|
||||
const { getByTestId } = await renderInTestApp(
|
||||
<Sidebar>
|
||||
<SidebarItem icon={HomeIcon} to="/one" text="Home" />
|
||||
</Sidebar>,
|
||||
<SidebarPage>
|
||||
<Sidebar>
|
||||
<SidebarItem icon={HomeIcon} to="/one" text="Home" />
|
||||
</Sidebar>
|
||||
</SidebarPage>,
|
||||
);
|
||||
expect(getByTestId('mobile-sidebar-root')).toBeVisible();
|
||||
});
|
||||
|
||||
@@ -31,7 +31,12 @@ import { SidebarContext } from '.';
|
||||
import { sidebarConfig } from './config';
|
||||
import { SidebarGroup } from './SidebarGroup';
|
||||
|
||||
type MobileSidebarContextType = {
|
||||
/**
|
||||
* Type of `MobileSidebarContext`
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type MobileSidebarContextType = {
|
||||
selectedMenuItemIndex: number;
|
||||
setSelectedMenuItemIndex: React.Dispatch<React.SetStateAction<number>>;
|
||||
};
|
||||
@@ -111,12 +116,25 @@ const OverlayMenu = ({
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Context on which `SidebarGroup` is currently selected
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const MobileSidebarContext = createContext<MobileSidebarContextType>({
|
||||
selectedMenuItemIndex: -1,
|
||||
setSelectedMenuItemIndex: () => {},
|
||||
});
|
||||
|
||||
/** @public */
|
||||
/**
|
||||
* A navigation component for mobile screens, which sticks to the bottom.
|
||||
*
|
||||
* It alternates the normal sidebar by grouping the `SidebarItems` based on provided `SidebarGroups`
|
||||
* either rendering them as a link or an overlay menu.
|
||||
* If no `SidebarGroups` are provided the sidebar content is wrapped in an default overlay menu.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const MobileSidebar = (props: React.PropsWithChildren<{}>) => {
|
||||
const { children } = props;
|
||||
const classes = useStyles();
|
||||
@@ -160,9 +178,8 @@ export const MobileSidebar = (props: React.PropsWithChildren<{}>) => {
|
||||
>
|
||||
<OverlayMenu
|
||||
label={
|
||||
sidebarGroups[selectedMenuItemIndex]
|
||||
? (sidebarGroups[selectedMenuItemIndex].props.label as string)
|
||||
: ''
|
||||
sidebarGroups[selectedMenuItemIndex] &&
|
||||
(sidebarGroups[selectedMenuItemIndex]!.props.label as string)
|
||||
}
|
||||
open={shouldShowGroupChildren}
|
||||
onClose={() => setSelectedMenuItemIndex(-1)}
|
||||
|
||||
@@ -47,14 +47,22 @@ const useStyles = makeStyles<BackstageTheme, { isPinned: boolean }>(
|
||||
{ name: 'BackstageSidebarPage' },
|
||||
);
|
||||
|
||||
/** @public */
|
||||
/**
|
||||
* Type of `SidebarStateContext`
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type SidebarStateContextType = {
|
||||
isPinned: boolean;
|
||||
toggleSidebarPinState: () => any;
|
||||
isMobile: boolean;
|
||||
};
|
||||
|
||||
/** @public */
|
||||
/**
|
||||
* Contains the state on how the `Sidebar` is rendered
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const SidebarStateContext = createContext<SidebarStateContextType>({
|
||||
isPinned: true,
|
||||
toggleSidebarPinState: () => {},
|
||||
|
||||
@@ -20,32 +20,42 @@ import LayersIcon from '@material-ui/icons/Layers';
|
||||
import LibraryBooks from '@material-ui/icons/LibraryBooks';
|
||||
import { fireEvent } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { SidebarItem } from './Items';
|
||||
import { MobileSidebarContext } from './MobileSidebar';
|
||||
import { SidebarGroup } from './SidebarGroup';
|
||||
import {
|
||||
MobileSidebarContext,
|
||||
SidebarGroup,
|
||||
SidebarItem,
|
||||
SidebarPage,
|
||||
} from '.';
|
||||
|
||||
const SidebarGroupWithItems = () => (
|
||||
<SidebarGroup icon={<HomeIcon />} label="Menu">
|
||||
<SidebarItem icon={HomeIcon} to="/one" text="Home" />
|
||||
<SidebarItem icon={LayersIcon} to="/two" text="Explore" />
|
||||
<SidebarItem icon={LibraryBooks} to="/three" text="Docs" />
|
||||
</SidebarGroup>
|
||||
<SidebarPage>
|
||||
<SidebarGroup icon={<HomeIcon />} label="Menu">
|
||||
<SidebarItem icon={HomeIcon} to="/one" text="Home" />
|
||||
<SidebarItem icon={LayersIcon} to="/two" text="Explore" />
|
||||
<SidebarItem icon={LibraryBooks} to="/three" text="Docs" />
|
||||
</SidebarGroup>
|
||||
</SidebarPage>
|
||||
);
|
||||
|
||||
describe('<SidebarGroup />', () => {
|
||||
it('should render Items in BottomNavigationAciton on small screens', async () => {
|
||||
it('should render Items in BottomNavigationAction on small screens', async () => {
|
||||
mockBreakpoint({ matches: true });
|
||||
const { findByRole, container } = await renderInTestApp(
|
||||
const { getByRole, getAllByRole } = await renderInTestApp(
|
||||
<SidebarGroupWithItems />,
|
||||
);
|
||||
expect(container.childNodes.length).toBe(1);
|
||||
expect(await findByRole('button')).toBeVisible();
|
||||
expect(getAllByRole('button').length).toEqual(1);
|
||||
expect(getByRole('button')).toBeVisible();
|
||||
});
|
||||
|
||||
it('should render Items without wrapper on bigger screens', async () => {
|
||||
mockBreakpoint({ matches: false });
|
||||
const { container } = await renderInTestApp(<SidebarGroupWithItems />);
|
||||
expect(container.childNodes.length).toBe(3);
|
||||
const { getByRole, queryByRole } = await renderInTestApp(
|
||||
<SidebarGroupWithItems />,
|
||||
);
|
||||
expect(queryByRole('button')).not.toBeInTheDocument();
|
||||
expect(getByRole('link', { name: 'Home' })).toBeVisible();
|
||||
expect(getByRole('link', { name: 'Explore' })).toBeVisible();
|
||||
expect(getByRole('link', { name: 'Docs' })).toBeVisible();
|
||||
});
|
||||
|
||||
it('should trigger update of MobileSidebarContext', async () => {
|
||||
|
||||
@@ -27,9 +27,20 @@ import { Link } from '../../components';
|
||||
import { sidebarConfig } from './config';
|
||||
import { MobileSidebarContext } from './MobileSidebar';
|
||||
|
||||
/** @public */
|
||||
/**
|
||||
* Props for the `SidebarGroup`
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface SidebarGroupProps extends BottomNavigationActionProps {
|
||||
/**
|
||||
* If the `SidebarGroup` should be a `Link`, `to` should be a pathname to that location
|
||||
*/
|
||||
to?: string;
|
||||
/**
|
||||
* If the `SidebarGroups` should be in a different order than in the normal `Sidebar`, you can provide
|
||||
* each `SidebarGroup` it's own priority to reorder them.
|
||||
*/
|
||||
priority?: number;
|
||||
}
|
||||
|
||||
@@ -51,6 +62,13 @@ const useStyles = makeStyles<BackstageTheme>(theme => ({
|
||||
},
|
||||
}));
|
||||
|
||||
/**
|
||||
* Returns a MUI `BottomNavigationAction`, which is aware of the current location & the selected item in the `BottomNavigation`,
|
||||
* such that it will highlight a `MobileSidebarGroup` either on location change or if the selected item changes.
|
||||
*
|
||||
* @param props `to`: pathname of link; `value`: index of the selected item
|
||||
* @internal
|
||||
*/
|
||||
const MobileSidebarGroup = (props: SidebarGroupProps) => {
|
||||
const { to, label, icon, value } = props;
|
||||
const classes = useStyles();
|
||||
@@ -88,7 +106,14 @@ const MobileSidebarGroup = (props: SidebarGroupProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
/** @public */
|
||||
/**
|
||||
* Groups items of the `Sidebar` together.
|
||||
*
|
||||
* On bigger screens, this won't have any effect at the moment.
|
||||
* On small screens, it will add an action to the bottom navigation - either triggering an overlay menu or acting as a link
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const SidebarGroup = (
|
||||
props: React.PropsWithChildren<SidebarGroupProps>,
|
||||
) => {
|
||||
|
||||
@@ -122,7 +122,6 @@ export const SidebarSubmenuItem = (props: SidebarSubmenuItemProps) => {
|
||||
const { title, to, icon: Icon, dropdownItems } = props;
|
||||
const classes = useStyles();
|
||||
const { setIsHoveredOn } = useContext(SidebarItemWithSubmenuContext);
|
||||
|
||||
const closeSubmenu = () => {
|
||||
setIsHoveredOn(false);
|
||||
};
|
||||
|
||||
@@ -58,11 +58,17 @@ export const submenuConfig = {
|
||||
export const SIDEBAR_INTRO_LOCAL_STORAGE =
|
||||
'@backstage/core/sidebar-intro-dismissed';
|
||||
|
||||
/**
|
||||
* Types for the `SidebarContext`
|
||||
*/
|
||||
export type SidebarContextType = {
|
||||
isOpen: boolean;
|
||||
setOpen?: (open: boolean) => void;
|
||||
};
|
||||
|
||||
/**
|
||||
* Context wether the `Sidebar` is open
|
||||
*/
|
||||
export const SidebarContext = createContext<SidebarContextType>({
|
||||
isOpen: false,
|
||||
});
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
*/
|
||||
|
||||
export { Sidebar } from './Bar';
|
||||
export { MobileSidebar } from './MobileSidebar';
|
||||
export { MobileSidebar, MobileSidebarContext } from './MobileSidebar';
|
||||
export type { MobileSidebarContextType } from './MobileSidebar';
|
||||
export { SidebarGroup } from './SidebarGroup';
|
||||
export type { SidebarGroupProps } from './SidebarGroup';
|
||||
export { SidebarSubmenuItem } from './SidebarSubmenuItem';
|
||||
|
||||
@@ -87,6 +87,8 @@ import {
|
||||
SidebarSpacerClassKey,
|
||||
SidebarDividerClassKey,
|
||||
SidebarIntroClassKey,
|
||||
SidebarItemClassKey,
|
||||
SidebarPageClassKey,
|
||||
CustomProviderClassKey,
|
||||
SignInPageClassKey,
|
||||
TabbedCardClassKey,
|
||||
@@ -162,6 +164,8 @@ type BackstageComponentsNameToClassKey = {
|
||||
BackstageSidebarSpacer: SidebarSpacerClassKey;
|
||||
BackstageSidebarDivider: SidebarDividerClassKey;
|
||||
BackstageSidebarIntro: SidebarIntroClassKey;
|
||||
BackstageSidebarItem: SidebarItemClassKey;
|
||||
BackstageSidebarPage: SidebarPageClassKey;
|
||||
BackstageCustomProvider: CustomProviderClassKey;
|
||||
BackstageSignInPage: SignInPageClassKey;
|
||||
BackstageTabbedCard: TabbedCardClassKey;
|
||||
|
||||
@@ -20,7 +20,7 @@ import { UserSettingsPinToggle } from './UserSettingsPinToggle';
|
||||
import { UserSettingsThemeToggle } from './UserSettingsThemeToggle';
|
||||
|
||||
export const UserSettingsAppearanceCard = () => {
|
||||
const isMobile = useContext(SidebarStateContext);
|
||||
const { isMobile } = useContext(SidebarStateContext);
|
||||
|
||||
return (
|
||||
<InfoCard title="Appearance" variant="gridItem">
|
||||
|
||||
Reference in New Issue
Block a user