-
Notifications
You must be signed in to change notification settings - Fork 874
feat: implement CRUD UI for IDP organization sync settings #15503
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
Conversation
42deaba
to
b632f87
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left a ton of comments, but I'm willing to budge on a lot of these.
I do have concerns about how some of the code in this PR reads. It doesn't really read like any of the code I've seen from you before, and there are definitely some logic bugs that seem almost like they were written by someone else. I don't know what parts are from Shad, and which aren't, but I am a little worried if this is the quality we're getting from Shad going forward
site/src/components/ui/badge.tsx
Outdated
extends React.HTMLAttributes<HTMLDivElement>, | ||
VariantProps<typeof badgeVariants> {} | ||
|
||
function Badge({ className, variant, ...props }: BadgeProps) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: one thing we've been doing recently is bringing in the FC
type to give better feedback about accidentally returning wrong values (especially since React 18 fixed the previous issues with the type)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, I think we should still use const
and FC
and arrow functions. it's a small bit of work to clean up shadcn things as we add them, but there are reasons we've consolidated on writing components the way that we do, and we should definitely stick with it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, seems like a good idea. This means I should probably create a doc to list all the extra steps needed when importing a shadcn component.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@aslilac it would be useful to know the various reasons that components are currently written as they are.
site/src/components/ui/badge.tsx
Outdated
); | ||
} | ||
|
||
export { Badge, badgeVariants }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure what we've been doing in core lately, so just making sure I understand: are we going to be exporting styles like this going forward? At first blush, it's not super clear to me if this will serve a benefit
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is definitely another shadcn-ism we should avoid. just put export
on the declaration if we need to. (I'm also not sure what value exporting a constant like this provides.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My original thought was to keep the shadcn components as close as possible to how they are imported but it makes sense to change this since the current codebase convention is not to group all exports at the bottom of the file. I think this pattern is generally used so its easy to look up all exports in one place.
@Parkreiner Since I have seen the export at the bottom pattern more often in other codebases, I am curious if you know why the current pattern exists?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be honest, I haven't been in enough "big" codebases to notice any "industry-leading" patterns, but Coder was just like this when I started working for them. Though I am a bigger fan of just exporting any relevant values directly
I know that .d.ts
files often have all their exports at the bottom, but I don't know if that's a meaningful reference, since those are auto-generated anyway
site/src/components/ui/command.tsx
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On a similar note to the one about the style exports, are we changing how we're writing out code now? I few things immediately jump out to me:
- Using a wildcard import for React (which shouldn't matter, since React isn't easy to tree-shake anyway)
- Consolidated exports at the end of the file
- Random library-specific HTML attribute (
cmdk-input-wrapper
) - Extensive use of Tailwind's arbitrary variants feature to inject styles for the Cmdk component
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Everything in src/components/ui
comes directly from shadcn except for multiple-selector
. I don't want to assume any existing patterns in the codebase are what we should definitely use going forward. if there is disagreement with how things are done with shadcn, lets bring them up all so I can make a list of everything that needs to be changed when importing a shadcn component.
site/src/components/ui/command.tsx
Outdated
return ( | ||
<Dialog {...props}> | ||
<DialogContent className="overflow-hidden p-0"> | ||
<Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like I'm missing something, but how are we formatting the Tailwind class names?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
uhh, this is a particularly nasty string of class names. it wraps across 4 lines on my enormous screen and immediately triggers my "this is incomprehensible and I should run away" reflex. how could we refactor this to be clearer? maybe for things with lots of complicated selectors like this we should stick with emotion actually?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All this is really doing is styling the underlying cmdk
component using the tailwind arbitrary variants feature. The purpose of doing this is because of the assumption that shadcn components are battle tested and are saving us alot of time compared to writing this from scratch. I don't think it would matter much if emotion was used here as the complexity exists because of consuming the cmdk
component.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, to be clear – I think this is an area where the Tailwind abstractions break down – definitely not the fault of anyone here. I understand why the code is the way it is:
- The CMDK component expects styles to be provided via a specific set of exposed class names
- We also can't dynamically process the class names in a function or something, because then Tailwind can't create CSS classes at compile time
- TW isn't great at that, so we have to bust out their arbitrary variants, and make sure that we're satisfying the TW compiler's needs, and the component's styling needs. It's just that the syntax is awkward and ugly
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not a fan of how it looks, but if we can shove all the nastiness into a corner and put a nice bow on it, I think it's fine enough
Getting back to my original question, though: what tools do we have to sort the class names automatically? The syntax is bad enough, but if we can't even order the styles by the order that they're applying in, this will be a nightmare to debug
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doing the class sorting is a solved problem when using prettier. Its a work in progress with biome as discussed here. biomejs/biome#1274
site/src/components/ui/dialog.tsx
Outdated
<DialogPrimitive.Overlay | ||
ref={ref} | ||
className={cn( | ||
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't the logic, obviously, but the fact that it's here has me worried our formatting logic isn't working properly: there's a double-space right after the bg-black/80
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right now biome doesn't help with this. Once this PR is merged I was planning on looking into the issue with Tailwind class sorting using Biome, biomejs/biome#1274
onKeyDown={(e) => { | ||
if (e.key === "Enter") { | ||
handleUnselect(option); | ||
} | ||
}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This shouldn't be necessary. The HTML spec makes sure that an onClick
handler is also automatically wired up to the onKeyDown
event for Enter and the onKeyUp
event for Space
The whole thing can be removed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When I test this by removing the onKeyDown
handler by tabbing to an item in the multi-select and pressing enter, I am no longer able to unselect/remove the item from the multi-select component.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, in that case, they might be doing some things that break from the spec. I'm okay with keeping it – I would just like a comment explaining that it is necessary
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Parkreiner Can you refer to where in the HTML spec you found this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
btw if I remove onKeyDown, the key that actually works in this case is space instead of enter
{/* biome-ignore lint/complexity/noUselessFragments: A parent element is | ||
needed for multiple dropdown items */} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you explain this comment a bit more? Is this a library need? And just making sure: is it okay that the element used is never rendered on screen?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Parkreiner I am only seeing line numbers for this comment and the line numbers have changed since the original comment. Which comment is this referring to?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did a quick second QA pass, and highlighted more concerns. Didn't have a ton of time, because I'm helping out with registry, but if you want to pair next week, just let me know
site/src/pages/DeploymentSettingsPage/IdpOrgSyncPage/IdpOrgSyncPageView.tsx
Outdated
Show resolved
Hide resolved
site/src/pages/DeploymentSettingsPage/IdpOrgSyncPage/IdpOrgSyncPage.tsx
Outdated
Show resolved
Hide resolved
site/src/pages/DeploymentSettingsPage/IdpOrgSyncPage/ExportPolicyButton.tsx
Outdated
Show resolved
Hide resolved
site/src/pages/DeploymentSettingsPage/IdpOrgSyncPage/IdpOrgSyncPageView.tsx
Outdated
Show resolved
Hide resolved
site/src/pages/DeploymentSettingsPage/IdpOrgSyncPage/IdpOrgSyncPageView.tsx
Outdated
Show resolved
Hide resolved
site/src/pages/DeploymentSettingsPage/IdpOrgSyncPage/IdpOrgSyncPageView.tsx
Show resolved
Hide resolved
site/src/pages/DeploymentSettingsPage/IdpOrgSyncPage/IdpOrgSyncPageView.tsx
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess one thing we should be clearer on: my vision for using shadcn was that we would perform heavy alterations to each component we bring in as necessary to make it feel cohesive with the rest of our codebase. Stuff like..
- file names
- code structure
- component definitions
- folder structure
etc. etc.
I'm also already really worried about the complexity of our tailwind class lists from this. I knew going in that using tailwind just kind of looks like this, but some of these are way gnarlier than I think should be acceptable for us.
site/src/components/ui/badge.tsx
Outdated
extends React.HTMLAttributes<HTMLDivElement>, | ||
VariantProps<typeof badgeVariants> {} | ||
|
||
function Badge({ className, variant, ...props }: BadgeProps) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, I think we should still use const
and FC
and arrow functions. it's a small bit of work to clean up shadcn things as we add them, but there are reasons we've consolidated on writing components the way that we do, and we should definitely stick with it.
site/src/components/ui/badge.tsx
Outdated
); | ||
} | ||
|
||
export { Badge, badgeVariants }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is definitely another shadcn-ism we should avoid. just put export
on the declaration if we need to. (I'm also not sure what value exporting a constant like this provides.)
site/src/components/ui/command.tsx
Outdated
return ( | ||
<Dialog {...props}> | ||
<DialogContent className="overflow-hidden p-0"> | ||
<Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
uhh, this is a particularly nasty string of class names. it wraps across 4 lines on my enormous screen and immediately triggers my "this is incomprehensible and I should run away" reflex. how could we refactor this to be clearer? maybe for things with lots of complicated selectors like this we should stick with emotion actually?
@@ -32,6 +32,7 @@ module.exports = { | |||
primary: "hsl(var(--surface-primary))", | |||
secondary: "hsl(var(--surface-secondary))", | |||
tertiary: "hsl(var(--surface-tertiary))", | |||
quaternary: "hsl(var(--surface-quaternary))", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was already against primary
, secondary
and tertiary
as being three bad names, but now we're adding quaternary
? this is such a horribly meaningless name
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At this point, the best place to discuss the naming issues would be in Figma as that is the source of truth. https://www.figma.com/design/WfqIgsTFXN2BscBSSyXWF8/Coder-kit?node-id=6-2115&m=dev
site/src/pages/DeploymentSettingsPage/IdpOrgSyncPage/PillList.tsx
Outdated
Show resolved
Hide resolved
<Pill | ||
key={organization} | ||
className={cn("border-none w-fit", { | ||
"bg-surface-error": UUID.test(organization), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wait, so it's an error to specify the UUID? why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is the same behavior as the idp sync groups and roles feature that went out with orgs. The idea is that if the uuid does not map to an organization, then the organization has been deleted and this is a problem that should be corrected.
0ea6dee
to
ae30db0
Compare
@@ -0,0 +1,80 @@ | |||
import type { Meta, StoryObj } from "@storybook/react"; | |||
import { Trash } from "lucide-react"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does this bring in all of lucide? how does this affect our bundle size?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only the icons that are used, https://lucide.dev/guide/#code-optimization
site/src/pages/DeploymentSettingsPage/IdpOrgSyncPage/IdpOrgSyncPage.tsx
Outdated
Show resolved
Hide resolved
site/src/pages/DeploymentSettingsPage/IdpOrgSyncPage/IdpOrgSyncPage.tsx
Outdated
Show resolved
Hide resolved
site/src/pages/DeploymentSettingsPage/IdpOrgSyncPage/OrganizationPills.tsx
Outdated
Show resolved
Hide resolved
site/src/pages/DeploymentSettingsPage/IdpOrgSyncPage/IdpOrgSyncPageView.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still have feedback and things I'd like changed, but nothing to block you.
site/src/pages/DeploymentSettingsPage/IdpOrgSyncPage/IdpOrgSyncPage.tsx
Outdated
Show resolved
Hide resolved
if (isLoading) { | ||
return <Loader />; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The more I think about this, the more I think we should be giving the user a better experience while the page is in a loading state.
Right now, while the entire page is loading, the user only sees a loading spinner, and because updating the title is tied to Helmet
, the HTML title stays the same from the previous page, too.
Ideally, I think I'd prefer for the page to be updated so that:
- The HTML title, page title, and description are updated immediately, since none of that requires fetching. All the data is available synchronously
- Move the loader inside the main content area, and also update the button so that it's more granularly tied to the loading state, too
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be honest, I feel like we're also kind of blurring what should be a page component concern, and what should be a view component concern
All the header content here is part of the UI view, but because it's not part of the view component, any errors or rendering problems will never show up in Storybook
Not sure if you have any thoughts @aslilac
<header className="flex flex-row items-baseline justify-between"> | ||
<div className="flex flex-col gap-2"> | ||
<h1 className="text-3xl m-0">Organization IdP Sync</h1> | ||
<p className="flex flex-row gap-1 text-sm text-content-secondary font-medium m-0"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just making sure: this is the only description in the Deployment navigation section that has a medium font weight. Is that something we're moving towards with the design tokens?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in the designs, most fonts are 500 but on this page it only needs to be specified here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes there is an issue already created to handle global style changes
* @see {@link https://ui.shadcn.com/docs/components/dialog} | ||
*/ | ||
import * as DialogPrimitive from "@radix-ui/react-dialog"; | ||
import { X } from "lucide-react"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: one thing I usually do when I import icons is that I rename them to SomethingIcon
, where Something
is the role I'm intending for the icon to fill. I find this really helpful when I'm using an icon outside its intended purpose, but even here, I think <CloseIcon />
is more readable than <X />
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there are pros and cons either way. I like to easily tell what the icon visually looks like and the original name works well for that. Using icon in the name could also be a good way to look up all icons. Overall, I am not a fan of naming imported components for their specific purpose unless its really confusing otherwise.
resolves coder/internal#205
The goal is to create a new page located in deployment settings to allow users to create and update organization IDP sync settings.