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

feat: adds app switcher lab component #2416

Merged
merged 4 commits into from
Nov 20, 2024
Merged

Conversation

michaeltamaki-okta
Copy link
Contributor

@michaeltamaki-okta michaeltamaki-okta commented Nov 15, 2024

OKTA-825118

Summary

feat: adds app switcher lab component

Building out the presentation layer of the "App Switcher" https://odyssey.okta.design/latest/components/app-switcher/design-mla5Wn5G (now called "Nav Rail").
Figma: https://www.figma.com/design/zh7A9gjaDlI50Hctdvd1OB/Odyssey-Components?node-id=22141-142254&node-type=frame&t=Qc7MjiWUFNtOh9Vd-0
Playground: https://6485e4e33982ca4977fff8d81ef98f680e713a9c.ods.dev/?path=/docs/labs-components-appswitcher--docs

Component checklist

Code structure

  • Organize code using a modular component architecture.
  • Use semantic HTML for clear and meaningful structure.
  • Break down complex components into smaller, reusable parts.

Accessibility implementation

  • Apply ARIA attributes to indicate roles and relationships.
  • Test focus management and ensure interactive elements are keyboard accessible.
  • Provide visual alternatives for auditory elements.

Internationalization (i18n) implementation

  • Separate any text content from the component's code for translation.
  • Test RTL support in Storybook
Screenshot 2024-11-18 at 8 44 24 AM

Cross-browser compatibility

  • Ensure consistent behavior and appearance across browsers.

Chrome

Screenshot 2024-11-15 at 10 14 34 AM Screenshot 2024-11-15 at 10 14 45 AM

Safari

Screenshot 2024-11-15 at 10 15 13 AM Screenshot 2024-11-15 at 10 15 21 AM

Firefox

Screenshot 2024-11-15 at 10 17 37 AM Screenshot 2024-11-15 at 10 17 43 AM

Documentation

  • Include code snippets, demos, and examples for various use cases.
  • Provide guidance on customization and handling special cases.

Finalize code

  • Interaction designers, UI eng and visual designers need to review that the Storybook code matches the intended Figma design
  • Interaction designers need to confirm Storybook code matches Supernova
  • Interaction and visual designers merge Figma and code at the same time
  • Share updates to the #design-system-team

QA

  • Include necessary unit tests to continuously verify component functionality
  • Screen reader test error/success states or message
  • Test edge cases and error scenarios to validate robustness

Testing and validation

  • Share work in Craft to get feedback from the Odyssey team
  • Share work to get feedback from pillar design captains or product designers in #design-team as customer feedback is essential to help us validate our assumptions, identify pain points, and improve our user experience

@michaeltamaki-okta michaeltamaki-okta requested a review from a team as a code owner November 15, 2024 21:33
Copy link
Contributor Author

@michaeltamaki-okta michaeltamaki-okta left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added notes from meeting with @KevinGhadyani-Okta

const renderAppIconLink = useCallback(
(muiProps: MuiPropsContextType) => {
return (
<NavRailAppLinkComponent
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add aria-current

>
<OktaAura />
</NavRailOktaAuraWrapperComponent>
{appIcons.map((appIcon) => (
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add nav (Add to the NavRail wrapper), ul (Add a wrapper over all the apps), li (Add to NavRailApp component)

shouldForwardProp: (prop) => prop !== "odysseyDesignTokens",
})(({ odysseyDesignTokens }: { odysseyDesignTokens: DesignTokens }) => ({
width: "100%",
height: NAV_RAIL_WIDTH,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See if we can import a shared value between top nav

Comment on lines 22 to 23
const APP_SIDE_LENGTH = "36px";
const APP_ICON_SIDE_LENGTH = "32px";
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change to rem

const NAV_RAIL_WIDTH = "64px";

export type NavRailProps = {
appIcons?: Omit<NavRailAppProps, "selectedAppName">[];
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer using Pick instead of Omit

Comment on lines 88 to 89
(muiProps: MuiPropsContextType) => {
return (
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

implicit return

>
<NavRailAppImgComponent
src={isSelected ? appIconSelectedUrl : appIconDefaultUrl}
alt="" // Tell screen reader to ignore the image; the Tooltip describes the link
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

role=presentation


const NAV_RAIL_WIDTH = "64px";

export type NavRailProps = {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add visibilityState = invisible | loading | loaded

How should we handle the loading state?

Skeleton

  • Enduser: Skeleton shows, then disappears (pop in during load)
  • Admin: Skeleton shows, then gets populated

No Skeleton ✅

  • Enduser: Skeleton does not show, then disappears
  • Admin: Skeleton does not show, then nav rail pops in
  • Could we mitigate this via storing in local storage to prevent pop in? Would pop in the first load, but would not pop in after that.
  • Also consider that this is just for the MVP where it is only shown for the admin. In the future, when it is shown to all users, we could switch to skeleton.

Full screen loading screen (prevents pop in)

  • Would block showing the app until we get the data. Unacceptable to downstream teams.

Keep side nav the same width regardless of app switcher

  • No, would break design assumptions

play: async ({ canvasElement, step }: PlaywrightProps<NavRailProps>) => {
const canvas = within(canvasElement);

await step("Nav rail successfully loads", async () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shows all items

play: async ({ canvasElement, step }: PlaywrightProps<NavRailProps>) => {
const canvas = within(canvasElement);

await step("Nav rail does not load", async () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hides when no items

@michaeltamaki-okta michaeltamaki-okta changed the title feat: adds nav rail lab component feat: adds app switcher lab component Nov 18, 2024
@michaeltamaki-okta michaeltamaki-okta force-pushed the mt-nav-rail branch 3 times, most recently from 6485e4e to 503f6d7 Compare November 20, 2024 01:26
} from "./AppSwitcherApp";
import { TOP_NAV_HEIGHT } from "../TopNav";

const APP_SWITCHER_WIDTH = `${64 / 14}rem`;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@michaeltamaki-okta Is this value meant to scale with the font size? Or, should it be a static px value?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it should be a rem value, we should probably use one of our spacing tokens here

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would be Spacing9 I think. The same as top nav.

display: "flex",
alignItems: "center",
justifyContent: "center",
marginBottom: odysseyDesignTokens.Spacing4,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
marginBottom: odysseyDesignTokens.Spacing4,
marginBlockEnd: odysseyDesignTokens.Spacing4,


const APP_SIDE_LENGTH_VAL = 36;
const APP_SIDE_LENGTH_REM = `${APP_SIDE_LENGTH_VAL / 14}rem`;
const APP_ICON_SIDE_LENGTH = `${32 / 14}rem`;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably be using token values for these. If we don't have token values for these sizes already, the sizes are likely incorrect. We'd have to go back to design and tell them we're using a token that already exists

alignItems: "center",
justifyContent: "center",
margin: 0,
width: APP_SIDE_LENGTH_REM,
Copy link
Contributor

@bryancunningham-okta bryancunningham-okta Nov 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The img size + the padding should be setting the width/height on these. We shouldn't need to set an explicit size here, I wouldn't think

benschell-okta
benschell-okta previously approved these changes Nov 20, 2024
Copy link
Contributor

@benschell-okta benschell-okta left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not feel qualified to review the core component, but the changes UiShell looks exactly like I hoped/expected (and that's primarily what my approval relates to)! Great work!

</AppSwitcherOktaAuraWrapperComponent>
<AppSwitcherAppIconULComponent>
{isLoading
? [...Array(3)].map(() => <AppSwitcherAppSkeleton />)
Copy link
Contributor

@KevinGhadyani-Okta KevinGhadyani-Okta Nov 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer this syntax:

Suggested change
? [...Array(3)].map(() => <AppSwitcherAppSkeleton />)
? Array(3).fill().map(() => <AppSwitcherAppSkeleton />)

I don't like using [...something] when we could use a semantic method like .slice() or .concat instead. Your intent is more clear that way.

selectedAppName,
}: AppSwitcherAppProps) => {
const odysseyDesignTokens = useOdysseyDesignTokens();
const isSelected = appName === selectedAppName;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You got selectedAppName in the API?

Copy link
Contributor Author

@michaeltamaki-okta michaeltamaki-okta Nov 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'd have to include this as part of renderUiShell, it is not returned in the API response

Comment on lines 116 to 128
await expect(canvas.getAllByRole("navigation")).toHaveLength(1);
await expect(canvas.queryAllByRole("listitem")).toHaveLength(0);
await expect(canvas.queryAllByRole("link")).toHaveLength(0);
});
Copy link
Contributor

@KevinGhadyani-Okta KevinGhadyani-Okta Nov 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can check on the navigation .toBeVisible() which is more accurate to your intent. You don't care if it's in the DOM.

I wonder if navigations should have an aria label because someone's not gonna know what they're selecting. I think there's a difference between primary and secondary navigations in ARIA. Something you'll wanna check into.

If so, then you'd do getByRole("navigation", { name: "YOUR_LABEL" }).

Copy link
Contributor

@KevinGhadyani-Okta KevinGhadyani-Okta left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! I made a few small comments.

@oktapp-aperture-okta oktapp-aperture-okta bot merged commit 1b48073 into main Nov 20, 2024
1 check passed
@oktapp-aperture-okta oktapp-aperture-okta bot deleted the mt-nav-rail branch November 20, 2024 22:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants