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

fix: Onboarding page added aria-lable and keyboard navigation #1562

Merged
merged 10 commits into from
Nov 10, 2023

Conversation

harsh9975
Copy link
Contributor

What does this PR do?

This PR was regarding issue #1552 added aria-label in all inputs and #1551 implemented keyboard navigation
however there is a small doubt
in radio group button while navigating through the page it only focuses on 1st radio button, in-order to navigate in all radio buttons I used useRef.
if there are any changes or suggestion you can guide me.

Fixes # (issue)
#1551
#1552

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • Chore (refactoring code, technical debt, workflow improvements)
  • Enhancement (small improvements)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change adds a new database migration
  • This change requires a documentation update

How should this be tested?

-create an account

Checklist

Required

  • Filled out the "How to test" section in this PR
  • Read How we Code at Formbricks
  • Self-reviewed my own code
  • Commented on my code in hard-to-understand bits
  • Ran pnpm build
  • Checked for warnings, there are none
  • Removed all console.logs
  • Merged the latest changes from main onto my branch with git pull origin main
  • My changes don't cause any responsiveness issues
  • First PR at Formbricks? Please sign the CLA! Without it we wont be able to merge it 🙏

Appreciated

  • If a UI change was made: Added a screen recording or screenshots to this PR
  • Updated the Formbricks Docs if changes were necessary

Copy link

vercel bot commented Nov 2, 2023

@harsh9975 is attempting to deploy a commit to the formbricks Team on Vercel.

A member of the Team first needs to authorize it.

Copy link
Contributor

github-actions bot commented Nov 2, 2023

Thank you for following the naming conventions for pull request titles! 🙏

Copy link
Contributor

packages/ui/ColorPicker/index.tsx, apps/web/app/(app)/onboarding/components/Product.tsx

Adding aria-labels to input fields can improve accessibility by providing more context to screen readers. This can be particularly helpful for users who rely on assistive technologies.
Create Issue

    <HexColorInput
      className="ml-2 mr-2 h-10 w-32 flex-1 border-0 bg-transparent text-slate-500 outline-none focus:border-none"
      color={color}
      onChange={onChange}
      id="color"
      aria-label="Primary color"
    />
    <Input
      id="product"
      type="text"
      placeholder="e.g. Formbricks"
      value={name}
      onChange={handleNameChange}
      aria-label="Your product name"
    />

apps/web/app/(app)/onboarding/components/Greeting.tsx

Adding keyboard accessibility to buttons can improve the user experience for those who rely on keyboard navigation. This can be achieved by adding a keydown event listener that triggers the button's action when the 'Enter' key is pressed.
Create Issue

    useEffect(() => {
      const handleKeyDown = (event: KeyboardEvent) => {
        if (event.key === "Enter") {
          event.preventDefault();
          next();
        }
      };
      const button = buttonRef.current;
      if (button) {
        button.focus();
        button.addEventListener("keydown", handleKeyDown);
      }

      return () => {
        if (button) {
          button.removeEventListener("keydown", handleKeyDown);
        }
      };
    }, []);

apps/web/app/(app)/onboarding/components/Role.tsx

Adding keyboard accessibility to radio buttons can improve the user experience for those who rely on keyboard navigation. This can be achieved by adding a keydown event listener that changes the focus and selected value when the 'Tab' key is pressed.
Create Issue

    useEffect(() => {
      const handleKeydown = (event: KeyboardEvent) => {
        if (event.key === "Tab") {
          event.preventDefault();
          const radioButtons = fieldsetRef.current?.querySelectorAll('input[type="radio"]');
          if (radioButtons && radioButtons.length > 0) {
            const focusedRadioButton = fieldsetRef.current?.querySelector(
              'input[type="radio"]:focus'
            ) as HTMLInputElement;
            if (!focusedRadioButton) {
              // If no radio button is focused, by default the first element will be focused
              const firstRadioButton = radioButtons[0] as HTMLInputElement;
              firstRadioButton.focus();
              setSelectedChoice(firstRadioButton.value);
            } else {
              const focusedIndex = Array.from(radioButtons).indexOf(focusedRadioButton);
              // If the last element is focused, set it back to the first one or change it to the next element
              if (focusedIndex === radioButtons.length - 1) {
                const firstRadioButton = radioButtons[0] as HTMLInputElement;
                firstRadioButton.focus();
                setSelectedChoice(firstRadioButton.value);
              } else {
                const nextRadioButton = radioButtons[focusedIndex + 1] as HTMLInputElement;
                nextRadioButton.focus();
                setSelectedChoice(nextRadioButton.value);
              }
            }
          }
        }
      };

      window.addEventListener("keydown", handleKeydown);

      return () => {
        window.removeEventListener("keydown", handleKeydown);
      };
    }, []);

Adding the htmlFor attribute to label elements can improve accessibility by creating an explicit association between the label and its corresponding input element. This can be particularly helpful for users who rely on assistive technologies.
Create Issue

    <label
      key={choice.id}
      htmlFor={choice.id}
      className={cn(
        selectedChoice === choice.label
          ? "z-10 border-slate-400 bg-slate-100"
          : "border-gray-200",
        "relative flex cursor-pointer flex-col rounded-md border p-4 hover:bg-slate-100 focus:outline-none"
      )}>
      <span className="flex items-center text-sm">
        <input
          type="radio"
          id={choice.id}
          value={choice.label}
          name="role"
          checked={choice.label === selectedChoice}
          className="checked:text-brand-dark  focus:text-brand-dark h-4 w-4 border border-gray-300 focus:ring-0 focus:ring-offset-0"
          aria-labelledby={`${choice.id}-label`}
          onChange={(e) => {
            setSelectedChoice(e.currentTarget.value);
          }}
          onKeyDown={(e) => {
            if (e.key === "Enter") {
              handleNextClick();
            }
          }}
        />
        <span id={`${choice.id}-label`} className="ml-3 font-medium">
          {choice.label}
        </span>
      </span>
    </label>

apps/web/app/(app)/onboarding/components/Objective.tsx

The current implementation of keyboard navigation is inefficient as it queries the DOM every time the 'Tab' key is pressed. This can be improved by storing the radio buttons in a state variable and updating it only when the component mounts or updates.
Create Issue

    const [radioButtons, setRadioButtons] = useState<NodeListOf<HTMLInputElement> | null>(null);

    useEffect(() => {
      setRadioButtons(fieldsetRef.current?.querySelectorAll('input[type="radio"]'));
    }, []);

    useEffect(() => {
      const handleKeydown = (event: KeyboardEvent) => {
        if (event.key === "Tab" && radioButtons) {
          event.preventDefault();
          const focusedRadioButton = document.activeElement as HTMLInputElement;
          if (!focusedRadioButton) {
            const firstRadioButton = radioButtons[0];
            firstRadioButton.focus();
            setSelectedChoice(firstRadioButton.value);
          } else {
            const focusedIndex = Array.from(radioButtons).indexOf(focusedRadioButton);
            if (focusedIndex === radioButtons.length - 1) {
              const firstRadioButton = radioButtons[0];
              firstRadioButton.focus();
              setSelectedChoice(firstRadioButton.value);
            } else {
              const nextRadioButton = radioButtons[focusedIndex + 1];
              nextRadioButton.focus();
              setSelectedChoice(nextRadioButton.value);
            }
          }
        }
      };

      window.addEventListener("keydown", handleKeydown);

      return () => {
        window.removeEventListener("keydown", handleKeydown);
      };
    }, [radioButtons]);

The code for handling the 'Tab' key press is quite long and complex. It can be simplified by extracting the logic for focusing on the next radio button into a separate function.
Create Issue

    const focusNextRadioButton = (radioButtons: NodeListOf<HTMLInputElement>, currentRadioButton: HTMLInputElement | null) => {
      const focusedIndex = currentRadioButton ? Array.from(radioButtons).indexOf(currentRadioButton) : -1;
      const nextIndex = (focusedIndex + 1) % radioButtons.length;
      const nextRadioButton = radioButtons[nextIndex];
      nextRadioButton.focus();
      setSelectedChoice(nextRadioButton.value);
    };

    useEffect(() => {
      const handleKeydown = (event: KeyboardEvent) => {
        if (event.key === "Tab" && radioButtons) {
          event.preventDefault();
          const focusedRadioButton = document.activeElement as HTMLInputElement;
          focusNextRadioButton(radioButtons, focusedRadioButton);
        }
      };

      window.addEventListener("keydown", handleKeydown);

      return () => {
        window.removeEventListener("keydown", handleKeydown);
      };
    }, [radioButtons]);

Copy link
Contributor

@Dhruwang Dhruwang left a comment

Choose a reason for hiding this comment

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

Hey @harsh9975 , functionality works very well 🚀😍, I just left a comment involving some refactoring tips. After those changes we are good to go !

apps/web/app/(app)/onboarding/components/Objective.tsx Outdated Show resolved Hide resolved
Copy link
Contributor

@Dhruwang Dhruwang left a comment

Choose a reason for hiding this comment

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

Thank you for the changes. Looks great 😊🚀

Copy link

vercel bot commented Nov 10, 2023

The latest updates on your projects. Learn more about Vercel for Git ↗︎

2 Ignored Deployments
Name Status Preview Comments Updated (UTC)
formbricks-cloud ⬜️ Ignored (Inspect) Visit Preview Nov 10, 2023 11:58am
formbricks-com ⬜️ Ignored (Inspect) Visit Preview Nov 10, 2023 11:58am

@Dhruwang Dhruwang added this pull request to the merge queue Nov 10, 2023
Merged via the queue into formbricks:main with commit 11ede2e Nov 10, 2023
9 checks passed
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.

None yet

2 participants