Here's an initial first pass of replacing the strategy lists in release
plan milestones.
The existing MilestoneCard has been moved to a Legacy file to avoid
conflicts.
This PR places the strategies in a list and changes the background color
of the list items (the strategies themselves still have a white
background, however).
It also re-orders the buttons in the footer and places the
milestone-level drag handle outside the milestone card.

## For later
Changing out the strategy list item itself hasn't been done yet. I want
to see if we can re-use the existing strategy draggable item instead of
making a copy. There's some dependencies on project path params etc that
need to be worked out first, though, so I'd prefer to do get these
initial changes through first.
Removes the `() => {} as any` args from the StrategyDraggableItem
invocation when you have paginated strats. Instead makes all the drag
params optional. It defaults to a no op if not provided.
Also, the reason it had to be typed as `any` before is probably because
it was missing a function. The correct empty param is `() => () => {}` 💁
Adjusts styling of the env dropdown now that we have both release plans
and strategies.
Key points:
- simplifies strategy separator, removes inherent height. Also: extracts
it from the draggable component (it has no business knowing whether to
add that or not)
- Puts release plans and strategies in the same list so that it becomes:
```markdown
- Release plan
- strategy 1
- strategy 2
- (OR) Strategy A
- (OR) Strategy B
```
- Adjusts some padding around to make it line up properly
- Swaps a couple conditional renders for ternaries
Rendered:

## Still todo:
Handle cases where you have >50 strats and we show the warning etc. It's
a little trickier because of how it interacts with release plans, so I
wanna leave that for later.
I'm also unsure about how we handle spacing today. All the little items
have their own different spacing and I'm not sure it won't get out of
sync, but I'm also not sure how else to handle it. We should look at it
later.
Splits the release plan component into a Legacy component and a new one
with the initial changes for the new strategy list view.
Here's what it looks like:

Notice that the background color stops a little early (before the OR
token). I'll handle that in a follow-up because the changes also impact
how the rest of the env accordion body is rendered.
Improves the semantic correctness of the strategy list by wrapping it
in an `ol` tag.
Strategy order matters (due to variant resolution etc), so the order
is important (hence the `ol` instead of a `ul`).
Dragging still works and it's visually the same.
Updates the strategy list based on the new designs and moves the current
versions of the touched components into `Legacy...` files (the vast
majority of changes are that and updating imports). The relevant changes
to the components are listed in their original files.
Flag on:

Flag off:

## Next steps
There's two items to review for improving these current comments (also
noted inline):
- Whether to aria-hide the "or" separator or not (I need to read up a
bit and think whether it makes sense to show that or not)
- Changing the list of strategies into an actual ordered list (`ol`).
That'd reflect the semantics better.
Next would be checking the other places we use strategy lists and then
updating those too. In doing so, I might find that some things need to
be updated, but I'll handle those when I get there.
There's also handling release plans.
Lets you navigate to the top of the list when you're at the bottom,
and vice versa.
Arrow down at the end of the list takes you to the search field and
arrow up from the search field takes you to the end of the list.
Makes the env selector on the flag page act the same way as the env
selector on the new project page or any of the filterable buttons in the
new project/flag dialogs.
Also slightly changes the styles of the existing dropdown lists to bring
them in line with the new env selector (more padding, full-width
highlights).
Selector:

Project/flag creation:
Before:

After:

## Technical notes
I was a little unsure how best to share the padding/spacing styles
between the search field and popover at first (as was requested by UX).
The easiest way (and most compliant with how we do it today) was to
define the spacing in a variable and move the relevant components into
the same file.
However, I actually think that using a CSS variable (e.g.
`--popover-spacing`) would be "better" here, but we don't really use
them much, so I've left that out for now. That said, if you agree, I'd
be more than happy to use that instead 🙋🏼
Reduces the size of the tab buttons on the flag page:
- Sets the min width to 100px instead of 160px on md screens. No change
for smaller screens
- Removes the min-height restriction imposed by theme.ts for the tab
bar, instead relying on the tab buttons to determine the height
(effectively changes the height from 70px to 62px).
Additionally: fixes an issue where the action buttons would overlap with
the tab buttons before wrapping and makes the tab bar scrollable. I can
no longer reproduce the issue where the action buttons force the tab bar
to be too small, but even if they should do that now, the tab bar is
scrollable so the remaining tabs are still accessible.
Because we only override the tabs' min-width on wider screens and mui
sets a default min-width, I changed the `onNarrowHeader` function to
`onWideHeader` and adjusted the other components accordingly. As a
bonus, the tab width and header wrapping now happens at the same time 🥳
After the change:

## Accessibility
This PR also addresses some of the a11y issues with this tab bar, namely
that it adds an `aria-label`, as mentioned in the [MUI
docs](https://v5.mui.com/material-ui/react-tabs/#accessibility).
It does **not**, however, connect the tabs to their corresponding tab
panels. The main reason for this is that we're not using tab panels and
that they're spread over 4 different components. We're probably using
the tabs component for something it isn't really designed to do in this
way. (Arguably they should be links and not buttons, for instance.) I'm
not going to touch this now, because that would probably be a lot of
work and it's not something I expect the business would prioritize.
## Changing theme.ts
While it's tempting to go in and change the `min-height` in `theme.ts`,
that would potentially affect all the other tab bars we have (although
maybe not, because we set a different min height for the tabs
themselves), I want to leave that for now. There is apparently some work
being done/prepared for the tabs, so it's probably better to leave that
for then.
Makes it so that the actions/tabs wrap on narrow width screens.
Constraints:
- When wrapping, the actions should go **before** the tabs, when not
wrapping, they should be placed **after**
- Need to maintain a logical tab order for wrapping, so just using
`flex-flow: row wrap-reverse` doesn't work because the tab order will be
wrong
- When the elements switch, you shouldn't lose your tab place in the
document
This solution uses container queries to determine the container size and
uses that to set the wrapping. Falls back to media queries if container
queries aren't supported (it's supported on >93% of browsers according
to caniuse).
The wrapping points don't use predefined media queries because:
- containers don't care about the size of the screen. It's the intrinsic
size of the container that matters.
- wrapping at 900px seemed too far out if container queries are
unsupported. But it's a fallback, so we can switch to that if we want.
If your keyboard focus is on one of the actions on a wide screen, and
the screen goes narrow, your focus will still be after the tabs (staying
consistent), so tabbing to the next element will take you into the flag
details, while backtab takes you back to the tabs.
Before wrapping:

After wrapping:

## A note on accessibility:
The spec for flexbox (taken from [MDN's
doc](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_flexible_box_layout/Ordering_flex_items))
states:
> "Authors must not use order or the *-reverse values of
[flex-flow](https://developer.mozilla.org/en-US/docs/Web/CSS/flex-flow)/flex-direction
as a substitute for correct source ordering, as that can ruin the
accessibility of the document."
So even if wrap-reverse works visually, it's not a good solution for
this.
- Button to show and hide environments
- Refactored hook storing state of hidden environments
- Changed the way flag is triggered for feature overview
- Visual updates for new page look
---------
Co-authored-by: Thomas Heartman <thomas@getunleash.io>
This PR moves the flag page header into a separate file, so that the
overview file is more clearly focused on the overview.
Additionally, it moves the modals that are triggered from the header
into the new file. This should give a nice little performance boost, as
opening and closing these modals should no longer trigger a re-rendering
of the full flag overview page, only the header.
If the average number of days in a stage is 1, use `1 day` instead of
`1 days`.
Likewise, if your total number of archived flags is 1, use `1 flag
archived` instead of `1 flags archived`.
I grepped through the file, but couldn't find any other hardcoded
instances of "flags" or "days", so I think this is everything.
Fixes a bug where the dashboard would scroll you down from the top of
the page on load if your window was too short too see both the
selected flag and the selected project.
This solves it by immediately scrolling to the top of the page after
scrolling your selected element into view. Because this hook only runs
on page load, it shouldn't be safe. (At least I couldn't make this
misbehave with manual testing).
It also changes the list scroll behavior to scroll your selected item
to the top of the list instead of to the bottom (effectively). During
testing, that seems like a better solution to me.
## Background (or why do we auto-scroll here?)
The dashboard's flag and projects panels stores your last selection,
so that when you return to the page you'll be shown what you were
looking at last. This is especially useful if you have a lot of flags
but you're focusing on one in particular.
However, if you **do** have a lot of flags, then it's also quite
likely that your selection will be "below the fold" of the panel, and
you won't see your selected flag/project immediately in the
list (without scrolling).
It seemed like a nice UI affordance to automatically bring your
selected item into view (especially because without it, there's no way
to see what flag/project) you're looking at, so I added the
[`scrollIntoView`](https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView)
hook.
What I didn't realize, however, is that it scrolls all scrollable
ancestor containers, which means that if your screen is too short,
it'll scroll you down the page.
From my reading of the docs and some local testing, I don't think
there is a way to limit the scrolling to only the nearest ancestor, so
the easiest way to ensure that we're always at the top seemed to be to
just scroll to the immediately after.
Fixes the height discrepancy between add strategy and more strategies
buttons, both with and without the flag enabled.
The essence of the fix is to make the "more strategies" button's height
dynamic and grow to match the height of the other button.
Before (flag enabled):

After (flag enabled):

Before (flag disabled):

After (flag disabled):

As a bonus: also enables the ui font redesign flag for server-dev.
If you're very sharp-eyed, you might notice a few things:
1. There's more padding on the new button. This was done in concert with
UX when we noticed there was more padding on other buttons. So as a
result, we set the button type to the default instead of "small".
1. The kebab button isn't perfectly square with the flag on. There's a
few issues here, but essentially: to use `aspect-ratio: 1`, you need
either a height or a width set. Because we want everything here to be
auto-generated (use the button's intrinsic height), I couldn't make it
work. In the end, I think this is close enough. If you have other ideas,
you're very welcome to try and fix it.
Also, use extra css selectors to increase specificity so that this
takes precedence over the MUI themes.
I don't like that we need to do this weird selector thing, but hey, it
is what it is.
Adds the button styles that were removed from `app.css` into the
legacy theme file. These change very slightly when the flag is on, and
because the hardcoded `app.css` styles have been removed, we'll use
the legacy file as fallback.
Fixes a small number of accessibility issues that Firefox was
complaining about (and some that I noticed myself):
1. In `CommandBar.tsx`, use a `Label` element instead of aria-label. We
can hide it with the `ScreenReaderOnly` component.
2. Add an `aria-label` to the icon button in the sidebar. (side note:
should we do any fancy detection on whether it's cmd + b or ctrl+b
there? I think we do that in the command bar)
3. Update the playground guidance popper;
i. Add an aria-label to the icon button
ii. Make the popper a `Popover` instead. This fixes a few issues: It
wasn't possible to focus or close just using the keyboard before.
Because it didn't steal focus, it also meant that it'd cover other parts
of the page. Now it traps focus so you can navigate to the close button,
and escape will also close it for you.
iii. Remove aria-describedby. Using aria-describedby on the button would
mean that the **button** is described by its content, which seems wrong.
aria-describedby should also only be used for plain strings. Complex
markups isn't supported. For that aria-details is the right way to go.
But because the popover is only rendered when it's open, the details or
describedby link will point to nothing most of the time.
iv. In doing this, there is a slight change in the popover shadow (I
couldn't find onef of our shadows that did the same thing as before),
but it matches other popovers we have, such as on the data usage tab.
Before:

After:

The license check API call was giving me 404s in the console of the
OSS version of Unleash.
This changes the `useLicense` hook to use `useEnterpriseSWR` instead
of `useSWR` to avoid making the API call in the OSS version. This is
consistent with the `useLicenseCheck` hook in the same file.
Fixes a small visual glitch where the event timeline panel (which
usually doesn't have a bottom border on the summary) would get a
bottom border during the collapsing animation.
This happens because to make the border act as we want, we switch
between using the summary's bottom border and the content's top
border, and I'd only updated one of the borders to respect the new
design.
Extracts each panel into its own component for the personal dashboard.
This lets us use separate states for each panel, which in turn lets each
panel change its open / close state without causing the other panels to
re-render.
When you have a lot of flags and/or projects, the list to render becomes
very long, which causes performance problems, especially when you need
to rerender both flags and projects and the timeline whenever one of
them changes.
The problems were especially noticeable in Firefox for me. Even with
this, the event timeline is a little choppy. I suspect that's because of
it might take a long time to paint? But we can look into that later.
Also updates the dashboard state hook to let you only pass in the
flags/projects you want. We could extract this into three different
hooks that all use the same localhost key, but I'm not sure whether
that's better or worse 🤷🏼
Prevents tab from navigating you through the list of results. Instead
makes it so that the tab key always takes you to the next item in the
same hierarchy.
As a bonus: also automatically closes the menu when you navigate
away (the previous implementation has a bug where it wouldn't if you
shift-tab).
The behavior of not letting you navigate the list with tab is
consistent with native HTML select elements as well as MUI select
elements. You typically navigate them with the arrow keys.
https://linear.app/unleash/issue/2-2834/plausible
Adds the following Plausible events to the Release management feature:
- Add plan
- Start milestone
- Remove plan
- Create template
- Edit template
- Delete template
Moves the event timeline to the personal dashboard from the header when
the `frontendHeaderRedesign` flag is active.
When the flag is active, it also:
- hides the event timeline and corresponding button in the header
- renders the environment selector next to the time selector instead of
at the other end of the header

---------
Co-authored-by: Nuno Góis <github@nunogois.com>
Fixes the issue where the skip link wouldn't take you to the main
content of the page anymore.
Also includes a few related minor semantic and a11y improvements:
1. The `main` element now only surrounds the actual main content of the
page. The sidebar is nav content and shouldn't be within it. The easiest
way to do this was to change the element that was previously a `main` to
a `div` and make the main content wrapper a `main` instead.
2. Makes the skip link target visible when focused. But invisible
otherwise. This has two benefits:
1. It's immediately obvious that using the skip link has worked. It
tells you that it's at the start of the main content.
2. Because the link now has text, it can be targeted by link search
(e.g. in Firefox, press `'` to search for links (I use this **a lot**)),
making it super easy to move your focus to the main content directly.
(Yes, landmark navigation should also work here, though, especially with
the `main` change).
The implementation of UI considerations of the skip link are based on
the CSS-tricks article [a deep dive on skipping to
content](https://css-tricks.com/a-deep-dive-on-skipping-to-content/)
from 2021.
Here's what it looks like when you skip to content:

When it doesn't have focus, it's invisible.
Makes the data returned from the traffic search a union type to avoid
nasty object-is-undefined errors at runtime.
It requires more explicit handling, sure, but it means we don't need
to accept undefined.
Adds new monthly estimation functions that operate on raw usage data
instead of chart data. This brings those methods in line with the rest
of the traffic calculation functions that we have in that file and means
we can remove other external dependencies.
This is somewhat inspired by #9218, but not directly linked.
Updates the existing BillingDetails pages (pro and payg) to use the new
traffic search endpoint (and calculations) if the flag to do so is on.
Otherwise, it falls back to using the existing method.
I've extracted the overage calculation into a separate shared hook.
Implements a function that cleans and filters incoming data from the
new traffic API.
Specifically, it:
- Removes `/edge` data points
- Removes any data from before may 2024, which is the first full month
we have on record
Because all uses of the existing hook do this filtering themselves, I
have added the filtering at the hook level. This is to avoid
forgetting this filtering later. If we find out we need this data, we
can move the filtering.
Refactors the period selector component now that the design / system is
pretty much finished.
Main points are: change from using CSS selectors to using styled
components; use props instead of classes. This is in keeping with the
general Unleash approach.
There's two very slight visual changes here:
1. There is 4px of added space below the "range" "header" text.
2. The months in the grid are a little closer together and not as wide.
This is because we remove the explicit column gap due to the grid having
a set width. Previously the width was automatic, but because we want
this to line up with the button, we need to set the width explicitly on
both items. As such, with the padding, the grid was a little too wide,
so there was too little padding on the right. This rectifies that.
This PR refactors the `NetworkTrafficUsage.tsx` and `useTrafficData`
files a bit.
The primary objective was to make the network traffic usage component
easier to work with, so I suggest to the reviewer that they start there.
Part of that refactoring, was taking things out of the useTraffic hook
that didn't need to be there. In the end, I'd removed so much that I
didn't even need the hook itself in the new component, so I switched
that to a regular useState.
It made more sense to me to put some of the functions inside the hook
into a separate file and import them directly (because they don't rely
on any hook state), so I have done that and removed those functions from
the trafficData hook. In this case, I also moved the tests.
I have not added any new tests in this PR, but will do so in a
follow-up. The functions I intend to test have been marked as such.