-
Notifications
You must be signed in to change notification settings - Fork 1.3k
feat(site): add organizationId prop to OrganizationAutocomplete #23906
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
Changes from all commits
c879fed
e0c6e5a
5933b94
6f2ef59
778b9bf
03191e0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -26,13 +26,28 @@ type OrganizationAutocompleteProps = { | |
| id?: string; | ||
| required?: boolean; | ||
| check?: AuthorizationCheck; | ||
| /** | ||
| * Pre-selects an organization by ID. When provided, the | ||
| * displayed selection is derived from this prop. The parent | ||
| * is responsible for updating this prop in response to user | ||
| * selections via onChange. | ||
| * | ||
| * When combined with `check`, the ID must reference an org | ||
| * the user is authorized for — if the org fails the check, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P3 Em-dash character (U+2014) in new comments. Also on line 95. Project convention prohibits em-dashes. Replace with a comma, period, or restructure the sentence.
|
||
| * the button silently shows placeholder text without firing | ||
| * onChange(null). The follow-up full-object refactor | ||
| * (https://github.com/coder/internal/issues/1440) will | ||
| * address this. | ||
| */ | ||
| organizationId?: string; | ||
|
johnstcn marked this conversation as resolved.
|
||
| }; | ||
|
|
||
| export const OrganizationAutocomplete: FC<OrganizationAutocompleteProps> = ({ | ||
| onChange, | ||
| id, | ||
| required, | ||
| check, | ||
| organizationId, | ||
| }) => { | ||
| const [open, setOpen] = useState(false); | ||
| const [selected, setSelected] = useState<Organization | null>(null); | ||
|
|
@@ -66,18 +81,31 @@ export const OrganizationAutocomplete: FC<OrganizationAutocompleteProps> = ({ | |
| : []; | ||
| } | ||
|
|
||
| // Unfortunate: this useEffect sets a default org value | ||
| // if only one is available and is necessary as the autocomplete loads | ||
| // its own data. Until we refactor, proceed cautiously! | ||
| // In controlled mode, derive the displayed selection from the | ||
| // prop so we never need to sync prop → state via an effect. | ||
| // Note: when `check` is also provided, options may be empty | ||
| // until permissions load, causing a brief placeholder flash. | ||
| // The follow-up refactor (passing the full Organization object | ||
| // instead of just an ID) will eliminate this. | ||
| const displayedSelection = organizationId | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2 When Trace: parent passes If the consuming form (#23827) submits with the uncorrected The JSDoc (lines 35-39) documents this and defers to coder/internal#1440 ("full-object refactor"). That refactor changes the input type but doesn't add a notification mechanism. If the refactored component receives a full Organization object that fails the No story covers Suggestion: add a story with
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note PS. The comment on line 154 references
|
||
| ? (options.find((o) => o.id === organizationId) ?? null) | ||
| : selected; | ||
|
|
||
| // Auto-select when only one option exists. Only active in | ||
| // uncontrolled mode — when the parent controls the value via | ||
| // organizationId it is responsible for the initial selection. | ||
| useEffect(() => { | ||
| if (organizationId) { | ||
| return; | ||
| } | ||
| const org = options[0]; | ||
| if (options.length !== 1 || org === selected) { | ||
| if (options.length !== 1 || org.id === selected?.id) { | ||
| return; | ||
| } | ||
|
|
||
| setSelected(org); | ||
| onChange(org); | ||
| }, [options, selected, onChange]); | ||
| }, [options, selected, onChange, organizationId]); | ||
|
|
||
| return ( | ||
| <Popover open={open} onOpenChange={setOpen}> | ||
|
|
@@ -90,14 +118,16 @@ export const OrganizationAutocomplete: FC<OrganizationAutocompleteProps> = ({ | |
| data-testid="organization-autocomplete" | ||
| className="w-full justify-start gap-2 font-normal" | ||
| > | ||
| {selected ? ( | ||
| {displayedSelection ? ( | ||
| <> | ||
| <Avatar | ||
| size="sm" | ||
| src={selected.icon} | ||
| fallback={selected.display_name} | ||
| src={displayedSelection.icon} | ||
| fallback={displayedSelection.display_name} | ||
| /> | ||
| <span className="truncate">{selected.display_name}</span> | ||
| <span className="truncate"> | ||
| {displayedSelection.display_name} | ||
| </span> | ||
| </> | ||
| ) : ( | ||
| <span className="text-content-secondary"> | ||
|
|
@@ -121,7 +151,12 @@ export const OrganizationAutocomplete: FC<OrganizationAutocompleteProps> = ({ | |
| key={org.id} | ||
| value={`${org.display_name} ${org.name}`} | ||
| onSelect={() => { | ||
| setSelected(org); | ||
| // Only update internal state in uncontrolled mode. | ||
| // In controlled mode, displayedSelection is derived | ||
| // from the organizationId prop. | ||
| if (!organizationId) { | ||
| setSelected(org); | ||
| } | ||
| onChange(org); | ||
| setOpen(false); | ||
| }} | ||
|
|
@@ -134,7 +169,7 @@ export const OrganizationAutocomplete: FC<OrganizationAutocompleteProps> = ({ | |
| <span className="truncate"> | ||
| {org.display_name || org.name} | ||
| </span> | ||
| {selected?.id === org.id && ( | ||
| {displayedSelection?.id === org.id && ( | ||
| <Check className="ml-auto size-icon-sm shrink-0" /> | ||
| )} | ||
| </CommandItem> | ||
|
|
||
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.
P3 JSDoc opens with "Pre-selects" but the prop does continuous control, not one-time initialization.
A caller reading "Pre-selects an organization by ID" will think this is a
defaultValuethat can be overridden by user interaction. ThePreselectedOrgUserSelectsstory proves otherwise: the display reverts to the prop value after user selection. "Controls the selected organization by ID" matches the actual semantics. (Gon)