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

<InlineAutocomplete>: Add onSelectSuggestion() callback to <InlineAutocomplete /> to allow execution of additional code after an autocomplete item was selected. #3647

Merged
merged 14 commits into from
Sep 6, 2023
Merged
7 changes: 7 additions & 0 deletions .changeset/sharp-eels-study.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@primer/react': minor
---

Adds onSelectSuggestion callback to <InlineAutocomplete />

<!-- Changed components: _none_ -->
6 changes: 6 additions & 0 deletions src/drafts/InlineAutocomplete/InlineAutocomplete.docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@
"required": true,
"description": "Register the triggers that can cause suggestions to appear."
},
{
"name": "onSelectSuggestion",
"type": "(event: SelectSuggestionsEvent) => void",
"defaultValue": "",
"description": "Called when a suggestion is selected. This should be used only for performing side effects, not for modifying\nthe inserted text. Do not call `setState` in this handler or the user's cursor\nposition / undo history could be lost."
},
{
"name": "onShowSuggestions",
"type": "(event: ShowSuggestionsEvent) => void",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,36 @@ export const SingleLine = ({loading, tabInserts}: ArgProps) => {
)
}

export const OnSelectSuggestion = ({loading, tabInserts}: ArgProps) => {
const [suggestions, setSuggestions] = useState<Suggestions | null>(null)

const onShowSuggestions = (event: ShowSuggestionsEvent) => {
if (loading) {
setSuggestions('loading')
return
}

setSuggestions(filteredUsers(event.query).map(user => user.login))
}

return (
<FormControl>
<FormControl.Label>Inline Autocomplete Demo</FormControl.Label>
<FormControl.Caption>Try typing &apos;@&apos; to show user suggestions.</FormControl.Caption>
<InlineAutocomplete
triggers={[{triggerChar: '@'}]}
onSelectSuggestion={suggestion => window.alert(`Selected ${suggestion}`)}
suggestions={suggestions}
onShowSuggestions={onShowSuggestions}
onHideSuggestions={() => setSuggestions(null)}
tabInsertsSuggestions={tabInserts}
>
<TextInput sx={{lineHeight: 1.2}} />
</InlineAutocomplete>
</FormControl>
)
}

const UserSuggestion = ({user, ...props}: {user: User} & ActionListItemProps) => (
<ActionList.Item {...props}>
<ActionList.LeadingVisual>
Expand Down
16 changes: 15 additions & 1 deletion src/drafts/InlineAutocomplete/InlineAutocomplete.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {useSyntheticChange} from '../hooks/useSyntheticChange'
import {getAbsoluteCharacterCoordinates} from '../utils/character-coordinates'

import {
SelectSuggestionsEvent,
ShowSuggestionsEvent,
Suggestions,
SuggestionsPlacement,
Expand All @@ -26,6 +27,16 @@ export type InlineAutocompleteProps = {
* `suggestions` prop accordingly.
*/
onShowSuggestions: (event: ShowSuggestionsEvent) => void

/**
* Called when a suggestion is selected.
*
* @note This should be used only for performing side effects, not for modifying
* the inserted text. Do not call `setState` in this handler or the user's cursor
* position / undo history could be lost.
*/
onSelectSuggestion?: (event: SelectSuggestionsEvent) => void
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you please update src/drafts/InlineAutocomplete/InlineAutocomplete.docs.json with this new prop?

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 the file - was not able to confirm the change is correct.

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 is done and CI is green.


/** Called when suggestions should be hidden. Set `suggestions` to `null` in this case. */
onHideSuggestions: () => void
/**
Expand Down Expand Up @@ -95,6 +106,7 @@ const InlineAutocomplete = ({
suggestions,
onShowSuggestions,
onHideSuggestions,
onSelectSuggestion,
sx,
children,
tabInsertsSuggestions = false,
Expand Down Expand Up @@ -163,6 +175,8 @@ const InlineAutocomplete = ({
if (!inputRef.current || !showEventRef.current) return
const {query, trigger} = showEventRef.current

onSelectSuggestion?.({suggestion, trigger, query})

const currentCaretPosition = getSelectionStart(inputRef.current) ?? 0
const deleteLength = query.length + trigger.triggerChar.length
const startIndex = currentCaretPosition - deleteLength
Expand All @@ -184,7 +198,7 @@ const InlineAutocomplete = ({
})

/**
* Even thoughn we apply all the aria attributes, screen readers don't fully support this
* Even though we apply all the aria attributes, screen readers don't fully support this
* dynamic use case and so they don't have a native way to indicate to the user when
* there are suggestions available. So we use some hidden text with aria-live to politely
* indicate what's available and how to use it.
Expand Down
5 changes: 5 additions & 0 deletions src/drafts/InlineAutocomplete/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ export type Trigger = {
keepTriggerCharOnCommit?: boolean
}

export type SelectSuggestionsEvent = ShowSuggestionsEvent & {
/** The suggestion that was selected. */
suggestion: Suggestion
}

export type ShowSuggestionsEvent = {
/** The trigger that caused this query. */
trigger: Trigger
Expand Down