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

Refactor greedy navigation #3265

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open

Refactor greedy navigation #3265

wants to merge 8 commits into from

Conversation

davidrapson
Copy link
Contributor

@davidrapson davidrapson commented Mar 27, 2024

Summary

Introduces a new version of the greedy navigation component with greatly simplified code and improved behaviour.

Motivation for change

The greedy navigation component is one of our oldest components and was adapted from an external component. It's long been a pain point of the application as it is hard for the team to modify leading to patch work amends and redundant behaviour being maintained.

The main motivation for change is that the component uses the icon font for the toggle button and we'd like to get rid of this in favour of SVG icons for all components.

This requires change. The component is extremely hard to change so we should make the change easy by restructuring the component to only contain the behaviour we need and to match the rest of our components.

The old component also has a completely different API to the rest of our components so this is a good opportunity to make that consistent.

Improvements

Restructuring the component enables a number of improvements:

  1. Switches the icon to an SVG version (the original motivation for change)
  2. Improves keyboard and mouse support (tabbing and clicking in and out of the menu now behaves more consistently)
  3. Minimises flash of unstyled content (fix ported from the public website)
  4. Bundles welsh translations for UI text. Previously this had to be provided as part of the component initialisation (translations taken taken from the public website).
  5. Fixes mistranslation of aria-labels (in production when translated they are part welsh part english)
  6. Removed significant amounts of redundant behaviour that was brought over from the module we modified.

Keyboard support

The old component had partial keyboard support but it was patchy. You could trigger the menu via keyboard but in a non-default way by tabbing in (but only in one direction). The component used a mouse up event instead of a typical click so you couldn't control the button with the space bar like a regular button.

The new behaviour is:

  1. The more button acts as a regular button and can be controlled with the space bar
  2. When the navigation is open you can tab out of it in either direction and the navigation should close (existing behaviour but should work a bit more predictably in both directions)
  3. Clicking anywhere outside the navigation should close the navigation (existing behaviour)
  4. Pressing the escape key should close the navigation (existing behaviour)

Configuration free

The new component is configuration free. The old component provided a number of configuration options many of which were partially implemented or redundant.

The exception being label text which is now translated as part of the Rails template.

The new component is initialised like all our other components.

import { initNavigation } from '@citizensadvice/design-system/lib';
initNavigation();

Testing strategy

Previously we had a mix of tests split across jest and cypress. The jest tests ran assertions against internal methods rather than using testing library helpers. Using jest for component tests is limited as they have to work against static fixtures and don't run in a real browser. Our cypress tests run in a real browser and against real examples but previously only covered some behaviour but not the actual greedy nav logic.

I've taken the approach of keeping a basic jest test around to make sure the component isn't completely broken and then pushed the majority of the test scenarios into the cypress tests. These now cover the logic around moving items into the greedy navigation, keyboard interaction (within the limit of cypress' abilities), and translation behaviour.

Backwards compatibility

To make it easier to migrate to the new component I've kept the old entrypoint around which wraps the new implementation. This allows us to ship the new version and then remove the old entrypoint in a subsequent version.


The exploratory work for this was done in #3264 in case you want to see the process. Depends on #3260

@davidrapson davidrapson force-pushed the greedy-nav-refactor branch 2 times, most recently from b48faf2 to 3959295 Compare March 27, 2024 17:28
@davidrapson davidrapson force-pushed the typescript-cleanup branch 2 times, most recently from 99cc2a2 to 86e43ab Compare March 27, 2024 17:31
@davidrapson davidrapson marked this pull request as ready for review March 27, 2024 17:42
@davidrapson davidrapson requested a review from a team as a code owner March 27, 2024 17:42
@davidrapson davidrapson requested review from leeky, akamike, cnorthwood, davidsauntson and marianayovcheva and removed request for a team and leeky March 27, 2024 17:42
@davidrapson davidrapson added the docs preview If this is added a preview version of the docs site will be deployed label Mar 27, 2024
@davidrapson davidrapson force-pushed the greedy-nav-refactor branch 2 times, most recently from a40f707 to eec7980 Compare March 27, 2024 17:47
@davidrapson davidrapson marked this pull request as draft March 28, 2024 09:46
label: 'Components/Header',
label: 'Components/Header (search open)',
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This previously had the same name as another scenario, surprised it didn't clash before!

Comment on lines +10 to +11
.component-preview--navigation,
.component-preview--header {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated these rules so the header component examples also have their margin removed. This is why the header visual regression snapshots have updated for those examples too.


/** @define navigation */
// ============================================================================
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've moved all the navigation styles including single-use mixins into this file as they were previously split across three.

I've also expanded all the selectors so there's minimal shorthand. I've got a strong preference against them and this refactoring reminded me why: it's much nicer if you can just search for the whole classname.

@@ -0,0 +1,81 @@
/**
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've kept this around as a basic initialisation test but honestly given the cypress tests cover everything anyway and are much better suited I'm inclined to delete this completely? How do folks feel about that? It's a pretty fake test as it stands.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think getting rid of it is fine

return containerEl.clientWidth - padding;
}

function computeChildrenOffsetWidth(containerEl: HTMLElement) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This used to have a 10 pixel offset but the rationale in the comment wasn't clear what benefit it gave. We can add it back if others disagree.

@davidrapson davidrapson marked this pull request as ready for review March 28, 2024 12:54
@davidrapson davidrapson force-pushed the greedy-nav-refactor branch 2 times, most recently from 11cfee1 to 84ed690 Compare April 8, 2024 11:21
Base automatically changed from typescript-cleanup to main April 17, 2024 09:42
Currently we have a couple of papercuts with our packaged javascript
modules

1. The method of importing and initialising them is inconsistent
2. The typescript configuration needed is relatively complex for a small
   number of modules

This commit attempts to rationalise that so that we have a consistent
pattern for importing and initialising all modules and as a side-benefit
a simpler typescript configuration.

- Defines a top-level tsconfig.json that works for all modules and
  removes any per-module configuration needed
- Adds consistent index.ts files to every module with a flexible set of
  default and named exports
- Adds a wrapper method to greedy nav to allow it to be initialised in
  the same way as all other modules

The goal is to support the existing import paths whilst enabling a
consistent preferred method using named exports:

```
import { initMyModule } from
"@citizensadvice/design-system/lib/my-module"
```

I've also included a `lib/index.ts` file so you can import from a single
entrypoint:

```
import { initModuleA, initModuleB } from
"@citizensadvice/design-system/lib"
```

Ideally we'd support importing directly from
`@citizensadvice/design-system` but that needs further restructuring to
make this work so something for a future release.
Summary
=======

Introduces a new version of the greedy navigation component with
greatly simplified code and improved behaviour.

Motivation for change
=====================

The greedy navigation component is one of our oldest components and was
adapted from an external component. It's long been a pain point of the
application as it is hard for the team to modify leading to patch work
amends and redundant behaviour being maintained.

The main motivation for change is that the component uses the icon font
for the toggle button and we'd like to get rid of this in favour of SVG
icons for all components.

This requires change. The component is extremely hard to change so we
should make the change easy by restructuring the component to only
contain the behaviour we need and to match the rest of our components.

The old component also has a completely different API to the rest of our
components so this is a good opportunity to make that consistent.

Configuration free
==================

The new component is configuration free. The old component provided a
number of configuration options many of which were partially implemented
or redundant.

The exception being label text which is now translated as part of the
Rails template.

The new component is initialised like all our other components.

```js
import { initNavigation } from '@citizensadvice/design-system/lib';
initNavigation();
```

Improvements
============

Restructuring the component enables a number of improvements:

1. Switches the icon to an SVG version (the original motivation for
   change)
2. Improves keyboard and mouse support (tabbing and clicking in and out
   of the menu now behaves more consistently)
3. Minimises flash of unstyled content (fix ported from the public
   website)
3. Bundles welsh translations for UI text. Previously this had to be
   provided as part of the component initialisation (translations taken
   taken from the public website).
4. Fixes mistranslation of aria-labels (in production when translated
   they are part welsh part english)
5. Removed significant amounts of redundant behaviour that was brought
   over from the module we modified.

Backwards compatibility
=======================

To make it easier to migrate to the new component I've kept the old
entrypoint around which wraps the new implementation. This allows us to
ship the new version with everything need as part of the library and we
can remove the old entrypoint in a subsequent version.
Causes a conflict with the sticky navigation styles
Copy link

🚀 Netlify deployed citizens-advice-design-system as draft

https://661fa42e2b512217036c5028--citizens-advice-design-system.netlify.app

@citizensadvice citizensadvice deleted a comment from github-actions bot Apr 17, 2024
@citizensadvice citizensadvice deleted a comment from github-actions bot Apr 17, 2024
@citizensadvice citizensadvice deleted a comment from github-actions bot Apr 17, 2024
@citizensadvice citizensadvice deleted a comment from github-actions bot Apr 17, 2024
@citizensadvice citizensadvice deleted a comment from github-actions bot Apr 17, 2024
@citizensadvice citizensadvice deleted a comment from github-actions bot Apr 17, 2024
@citizensadvice citizensadvice deleted a comment from github-actions bot Apr 17, 2024
@citizensadvice citizensadvice deleted a comment from github-actions bot Apr 17, 2024
Copy link
Contributor

@davidsauntson davidsauntson left a comment

Choose a reason for hiding this comment

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

This looks good to me. Some minor comments, it would be good to document doesItFit() a bit more (and maybe call it something more appropriate (it seems to hide / show elements too - but I might have misunderstood!).

// We use https://github.com/dmtrKovalenko/cypress-real-events
// to approximate this but full keyboard testing should still be
// done manually when changing this behaviour.
context('when interacting with a keyboard', () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we also test tabbing into it? If it's too fraught at least some sort of note to test that behaviour manually too?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll need to double check again, I think the answer is still that cypress doesn't handle that well so yes maybe a document that is manual testing steps for greedy nav would be good.

@@ -7,17 +7,6 @@ body {
height: 100%;
}

// https://philipwalton.github.io/solved-by-flexbox/demos/sticky-footer/
Copy link
Contributor

Choose a reason for hiding this comment

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

was this causing problems with the navigation?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes as a side-effect of porting over the overflow fix from public-website rather than the refactor itself.

@@ -0,0 +1,81 @@
/**
Copy link
Contributor

Choose a reason for hiding this comment

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

I think getting rid of it is fine

return sum;
}

function doesItFit(containerEl: HTMLElement, breaks: number[]) {
Copy link
Contributor

Choose a reason for hiding this comment

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

What is breaks here? I think it has something to do with the cumulative widths of each nav link but it would be good to make it explicit (in a comment would be fine I think).

In fact some documentation for this function would be good - it's hard to infer the behaviour from the code fully.

Also it seems to do more than answer the question 'does it fit' - should it be broken up or renamed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good shout.

Short answer: Agreed, let's break this down more.

Longer answer: For context the the process I followed was basically to copy over the existing file and names and gradually refactor the implementation, rather than a ground-up rewrite. I'd bet on this being part that retains the most naming of the original.

Copy link
Contributor

Choose a reason for hiding this comment

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

OK - I'm happy for it to stay like this then and maybe this can be a smaller refactor later down the line. It would be good to have another look at it at some point tho.

display: none;
////
// Prevent overflowing navigation when loaded
// or when javascript is disable
Copy link
Contributor

Choose a reason for hiding this comment

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

disabled

Copy link
Contributor

@davidsauntson davidsauntson left a comment

Choose a reason for hiding this comment

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

Happy for doesItFit to be sorted out in another refactor later 👍🏻

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs preview If this is added a preview version of the docs site will be deployed
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants