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

Add topbar #1484

Merged
merged 15 commits into from
Dec 6, 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
21 changes: 21 additions & 0 deletions .storybook/modes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { destkop as theme } from "../src/theme";
import numberFromDimension from "../src/utils/numberFromDimension";

export const allModes = {
touch: {
locale: "en",
desktopScale: "standard",
theme: "touch",
viewports: [theme.breakpoints.small, theme.breakpoints.medium, theme.breakpoints.large].map(numberFromDimension),
},
desktop: {
locale: "en",
desktopScale: "standard",
theme: "desktop",
},
experimental: {
locale: "en",
desktopScale: "experimental",
theme: "desktop",
},
};
4 changes: 2 additions & 2 deletions .storybook/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from "react";
import { desktop as theme } from "../src/theme";
import { ALL_NDS_LOCALES, NDSProvider } from "../src";

const newViewports = {
const viewports = {
extraSmall: {
name: "Extra small",
styles: {
Expand Down Expand Up @@ -41,7 +41,7 @@ const newViewports = {
};

export const parameters = {
viewport: { viewports: newViewports },
viewport: { viewports },
layout: "padded",
options: {
storySort: {
Expand Down
51 changes: 51 additions & 0 deletions cypress/e2e/components/TopBar.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
describe("TopBar", () => {
const menuButton = () => cy.get('[data-testid="topbar-menu-button"]');
const menu = () => cy.get('[data-testid="topbar-menu"]');
const menuItems = () => cy.get('[data-testid="topbar-menu-item"]');
const menuOverlay = () => cy.get('[data-testid="topbar-menu-overlay"]');

describe("Menu", () => {
beforeEach(() => {
cy.renderFromStorybook("topbar--default");
});

it("opens menu when menu button is clicked", () => {
menuButton().click();
menu().should("be.visible");
});

it("closes menu when clicking outside", () => {
menuButton().click();
menuOverlay().should("be.visible");
menuOverlay().click({ force: true });
menu().should("not.exist");
});

it("displays correct number of menu items", () => {
menuButton().click();
menuItems().should("have.length", 9); // see Topbar/stories/fixtures.ts
});
});

describe("Accessibility", () => {
beforeEach(() => {
cy.renderFromStorybook("topbar--default");
});

it("focuses the first focusable element in the page", () => {
menuButton().focus().type("{enter}");
menuItems().first().find("a").should("have.focus");
});

it("maintains focus trap in menu", () => {
menuButton().click();
menuItems().last().find("a").focus().tab();
menuItems().first().find("a").should("have.focus");
});

it("has correct ARIA attributes", () => {
menuButton().click();
menu().should("have.attr", "aria-label", "Menu options");
});
});
});
1 change: 1 addition & 0 deletions locales/de_DE.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"close menu": "Menü schließen",
"menu options": "Menüoptionen",
"close": "Schließen",
"collapse row": "Zeile verbergen",
"date range": "Datumsbereich",
Expand Down
1 change: 1 addition & 0 deletions locales/en_US.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"close menu": "Close menu",
"menu options": "Menu options",
"close": "Close",
"collapse row": "Collapse row",
"date range": "Date range",
Expand Down
1 change: 1 addition & 0 deletions locales/es_MX.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"close menu": "Cerrar menú",
"menu options": "Opciones del menú",
"close": "Cerrar",
"collapse row": "Colapsar fila",
"date range": "Rango de fechas",
Expand Down
1 change: 1 addition & 0 deletions locales/fr_FR.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"close menu": "Fermer le menu",
"menu options": "Options du menu",
"close": "Fermer",
"collapse row": "Réduire la ligne",
"date range": "Plage de dates",
Expand Down
1 change: 1 addition & 0 deletions locales/nl_NL.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"close menu": "Menu sluiten",
"menu options": "Menu-opties",
"close": "Sluiten",
"collapse row": "Rij samenvouwen",
"date range": "Datumbereik",
Expand Down
1 change: 1 addition & 0 deletions locales/pl_PL.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"close menu": "Zamknij menu",
"menu options": "Opcje menu",
"close": "Zamknij",
"collapse row": "Zwiń wiersz",
"date range": "Zakres daty",
Expand Down
1 change: 1 addition & 0 deletions locales/pt_BR.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"close menu": "Fechar menu",
"menu options": "Opções do menu",
"close": "Fechar",
"collapse row": "Recolher fila",
"date range": "Intervalo de datas",
Expand Down
3 changes: 2 additions & 1 deletion locales/ro_RO.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"close menu": "Close menu",
"close menu": "Închidere meniu",
"menu options": "Opțiuni meniu",
"close": "Închidere",
"collapse row": "Restrângere rând",
"date range": "Interval dată",
Expand Down
1 change: 1 addition & 0 deletions locales/zh_CN.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"close menu": "关闭菜单",
"menu options": "菜单选项",
"close": "关闭",
"collapse row": "折叠行",
"date range": "日期范围",
Expand Down
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
"clean:storybook": "rm -rf node_modules/.cache/storybook && rm -rf ./storybook-static",
"build": "rollup -c",
"build:storybook": "build-storybook",
"check": "yarn check:types && yarn check:lint && yarn check:format",
"warn:prepush": "echo \"Make sure you also run all the other CI steps before pushing by running 'yarn ci'\"",
"check": "yarn warn:prepush && yarn check:types && yarn check:lint && yarn check:format",
"check:types": "tsc && cd cypress && tsc --noEmit",
"check:lint": "eslint --config ./.eslintrc .'*/**/*.{js,ts,tsx}'",
"check:format": "prettier -c .",
Expand Down Expand Up @@ -68,7 +69,7 @@
"@babel/preset-env": "7.3.1",
"@babel/preset-typescript": "^7.10.4",
"@nulogy/eslint-config-nulogy": "^1.0.0",
"@nulogy/icons": "^4.36.0",
"@nulogy/icons": "^4.37.2",
"@rollup/plugin-babel": "^5.0.0",
"@rollup/plugin-node-resolve": "^7.1.3",
"@semantic-release/changelog": "^6.0.2",
Expand Down Expand Up @@ -173,7 +174,7 @@
},
"husky": {
"hooks": {
"pre-push": "yarn run ci"
"pre-push": "yarn run check"
}
},
"jest": {
Expand Down
1 change: 0 additions & 1 deletion src/Link/Link.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import styled from "styled-components";
import { darken } from "polished";
import { themeGet } from "@styled-system/theme-get";
import { variant } from "styled-system";
import React from "react";
import { DefaultNDSThemeType } from "../theme";
import { addStyledProps, StyledProps } from "../StyledProps";
Expand Down
93 changes: 93 additions & 0 deletions src/TopBar/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# TopBar

The TopBar component provides users a consistent navigation experience across touch application pages.
It combines functionality from the desktop `Header` and the `BrandedNavBar` components.
The component is meant to be used in applications where the primary input device is a touch screen.

## Basic Usage

```tsx
import { TopBar } from '@nulogy/components';

function MyComponent() {
return (
<TopBar.Root>
<TopBar.BackLink href="/previous-page">Back</TopBar.BackLink>
<TopBar.PageTitle>Current Page</TopBar.PageTitle>
<TopBar.Menu>
<TopBar.MenuItem
title="Home"
description="Go to home page"
icon="home"
href="/home"
/>
</TopBar.Menu>
</TopBar.Root>
);
}
```

## Components

### TopBar.Root

The main container component that provides the header structure.

```tsx
<TopBar.Root>
{/* Other TopBar components */}
</TopBar.Root>
```

### TopBar.BackLink

Navigation component for going back to previous pages. Extends and accepts props for `React.ComponentProps<'a'>`. Can be customized with a different component using the `as` prop.

| Prop | Type | Description | Default |
|------|------|-------------|---------|
| `maxWidth` | ResponsiveValue | Controls the maximum width of the back label. Can be customized using any CSS unit such as `px`, `em`, `ch`, etc... | `{ phoneLandscape: "20ch", tabletPortrait: "18ch", tabletLandscape: "20ch", laptop: "24ch" }` |
| `children` | ReactNode | Optional label text (hidden on mobile) | - |

### TopBar.PageTitle

Displays the current page title with automatic text overflow handling.

| Prop | Type | Description |
|------|------|-------------|
| `children` | ReactNode | Title text |

### TopBar.Menu

A responsive menu system that displays as a grid of items in an overlay.

| Prop | Type | Description | Default |
|------|------|-------------|---------|
| `defaultOpened` | boolean | Controls if menu is open by default | `false` |
| `aria-label` | string | Descriptive name for the content of the menu | `t("menu options")` |
| `children` | ReactNode | MenuItem components | - |

### TopBar.MenuItem

Individual menu items with icons and descriptions. Extends and accepts props for `React.ComponentProps<'a'>`. Can be customized with a different component using the `as` prop.

| Prop | Type | Description |
|------|------|-------------|
| `title` | string | Item title |
| `description` | string | Optional description |
| `icon` | IconName | Icon to display |


## Accessibility

- Menu sets the focus to the first in the menu when opened, returns the focus to the menu trigger when closed
- When the Menu is opened, the focus is trapped within the menu
- Supports keyboard navigation
- Includes proper ARIA labels for the menu

## Best Practices

- Ensure back navigation is logical within the application flow, maintaining consisitency between the back and current page titles when navigating forward and backward.

## Technical Considerations

- The TopBar default to HTML anchor tags but can be customized to use React Router Link components using the `as` prop
Loading
Loading