From 8285f6248766c95896af6b6c768c2238dd54432a Mon Sep 17 00:00:00 2001 From: Patrick Dinger <121539+paxos@users.noreply.github.com> Date: Fri, 18 Aug 2023 12:01:46 -0700 Subject: [PATCH 01/12] Allows to customize the replaced text onCommit --- .../InlineAutocomplete.features.stories.tsx | 30 +++++++++++++++++++ .../InlineAutocomplete/InlineAutocomplete.tsx | 15 ++++++++-- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/src/drafts/InlineAutocomplete/InlineAutocomplete.features.stories.tsx b/src/drafts/InlineAutocomplete/InlineAutocomplete.features.stories.tsx index 3f2f9c5a6ae..0c4cc33a504 100644 --- a/src/drafts/InlineAutocomplete/InlineAutocomplete.features.stories.tsx +++ b/src/drafts/InlineAutocomplete/InlineAutocomplete.features.stories.tsx @@ -56,6 +56,36 @@ export const SingleLine = ({loading, tabInserts}: ArgProps) => { ) } +export const ReplaceSuggestion = ({loading, tabInserts}: ArgProps) => { + const [suggestions, setSuggestions] = useState(null) + + const onShowSuggestions = (event: ShowSuggestionsEvent) => { + if (loading) { + setSuggestions('loading') + return + } + + setSuggestions(filteredUsers(event.query).map(user => user.login)) + } + + return ( + + Inline Autocomplete Demo + Try typing '@' to show user suggestions. + setSuggestions(null)} + tabInsertsSuggestions={tabInserts} + onSelectSuggestion={suggestion => 'Something different'} + > + + + + ) +} + const UserSuggestion = ({user, ...props}: {user: User} & ActionListItemProps) => ( diff --git a/src/drafts/InlineAutocomplete/InlineAutocomplete.tsx b/src/drafts/InlineAutocomplete/InlineAutocomplete.tsx index f9d797823ad..824921f00d9 100644 --- a/src/drafts/InlineAutocomplete/InlineAutocomplete.tsx +++ b/src/drafts/InlineAutocomplete/InlineAutocomplete.tsx @@ -26,6 +26,12 @@ export type InlineAutocompleteProps = { * `suggestions` prop accordingly. */ onShowSuggestions: (event: ShowSuggestionsEvent) => void + + /** Called when a suggestion is selected. + * Allows to overwrite the value that is inserted into the wrapped component + */ + onSelectSuggestion?: (suggestion: string) => string + /** Called when suggestions should be hidden. Set `suggestions` to `null` in this case. */ onHideSuggestions: () => void /** @@ -95,6 +101,7 @@ const InlineAutocomplete = ({ suggestions, onShowSuggestions, onHideSuggestions, + onSelectSuggestion, sx, children, tabInsertsSuggestions = false, @@ -169,7 +176,11 @@ const InlineAutocomplete = ({ const keepTriggerChar = trigger.keepTriggerCharOnCommit ?? true const maybeTriggerChar = keepTriggerChar ? trigger.triggerChar : '' - const replacement = `${maybeTriggerChar}${suggestion} ` + let replacement = `${maybeTriggerChar}${suggestion} ` + + if (onSelectSuggestion) { + replacement = onSelectSuggestion(suggestion) + } emitSyntheticChange(replacement, [startIndex, startIndex + deleteLength]) onHideSuggestions() @@ -184,7 +195,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. From 1ba9b41ba7947101805f74f7715db7b94b369211 Mon Sep 17 00:00:00 2001 From: Patrick Dinger <121539+paxos@users.noreply.github.com> Date: Fri, 18 Aug 2023 12:23:24 -0700 Subject: [PATCH 02/12] Cleanup suggestion overwrite as this can be done by suggestion.value --- .../InlineAutocomplete.features.stories.tsx | 30 ------------------- .../InlineAutocomplete/InlineAutocomplete.tsx | 12 ++++---- 2 files changed, 6 insertions(+), 36 deletions(-) diff --git a/src/drafts/InlineAutocomplete/InlineAutocomplete.features.stories.tsx b/src/drafts/InlineAutocomplete/InlineAutocomplete.features.stories.tsx index 0c4cc33a504..3f2f9c5a6ae 100644 --- a/src/drafts/InlineAutocomplete/InlineAutocomplete.features.stories.tsx +++ b/src/drafts/InlineAutocomplete/InlineAutocomplete.features.stories.tsx @@ -56,36 +56,6 @@ export const SingleLine = ({loading, tabInserts}: ArgProps) => { ) } -export const ReplaceSuggestion = ({loading, tabInserts}: ArgProps) => { - const [suggestions, setSuggestions] = useState(null) - - const onShowSuggestions = (event: ShowSuggestionsEvent) => { - if (loading) { - setSuggestions('loading') - return - } - - setSuggestions(filteredUsers(event.query).map(user => user.login)) - } - - return ( - - Inline Autocomplete Demo - Try typing '@' to show user suggestions. - setSuggestions(null)} - tabInsertsSuggestions={tabInserts} - onSelectSuggestion={suggestion => 'Something different'} - > - - - - ) -} - const UserSuggestion = ({user, ...props}: {user: User} & ActionListItemProps) => ( diff --git a/src/drafts/InlineAutocomplete/InlineAutocomplete.tsx b/src/drafts/InlineAutocomplete/InlineAutocomplete.tsx index 824921f00d9..cfaaa1672c4 100644 --- a/src/drafts/InlineAutocomplete/InlineAutocomplete.tsx +++ b/src/drafts/InlineAutocomplete/InlineAutocomplete.tsx @@ -30,7 +30,7 @@ export type InlineAutocompleteProps = { /** Called when a suggestion is selected. * Allows to overwrite the value that is inserted into the wrapped component */ - onSelectSuggestion?: (suggestion: string) => string + onSelectSuggestion?: (suggestion: string) => void /** Called when suggestions should be hidden. Set `suggestions` to `null` in this case. */ onHideSuggestions: () => void @@ -170,17 +170,17 @@ const InlineAutocomplete = ({ if (!inputRef.current || !showEventRef.current) return const {query, trigger} = showEventRef.current + if (onSelectSuggestion) { + onSelectSuggestion(suggestion) + } + const currentCaretPosition = getSelectionStart(inputRef.current) ?? 0 const deleteLength = query.length + trigger.triggerChar.length const startIndex = currentCaretPosition - deleteLength const keepTriggerChar = trigger.keepTriggerCharOnCommit ?? true const maybeTriggerChar = keepTriggerChar ? trigger.triggerChar : '' - let replacement = `${maybeTriggerChar}${suggestion} ` - - if (onSelectSuggestion) { - replacement = onSelectSuggestion(suggestion) - } + const replacement = `${maybeTriggerChar}${suggestion} ` emitSyntheticChange(replacement, [startIndex, startIndex + deleteLength]) onHideSuggestions() From cc1b9a1f049981b4e32658553fe5abd7dca7f640 Mon Sep 17 00:00:00 2001 From: Patrick Dinger <121539+paxos@users.noreply.github.com> Date: Fri, 18 Aug 2023 12:30:15 -0700 Subject: [PATCH 03/12] Cleanup comment --- src/drafts/InlineAutocomplete/InlineAutocomplete.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/drafts/InlineAutocomplete/InlineAutocomplete.tsx b/src/drafts/InlineAutocomplete/InlineAutocomplete.tsx index cfaaa1672c4..bf3048f539b 100644 --- a/src/drafts/InlineAutocomplete/InlineAutocomplete.tsx +++ b/src/drafts/InlineAutocomplete/InlineAutocomplete.tsx @@ -28,7 +28,6 @@ export type InlineAutocompleteProps = { onShowSuggestions: (event: ShowSuggestionsEvent) => void /** Called when a suggestion is selected. - * Allows to overwrite the value that is inserted into the wrapped component */ onSelectSuggestion?: (suggestion: string) => void From e6bfe6ac9ebdd77199fe1dfe4ef9e5f2bcacf020 Mon Sep 17 00:00:00 2001 From: Patrick Dinger <121539+paxos@users.noreply.github.com> Date: Fri, 18 Aug 2023 15:17:42 -0700 Subject: [PATCH 04/12] Add changeset --- .changeset/sharp-eels-study.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/sharp-eels-study.md diff --git a/.changeset/sharp-eels-study.md b/.changeset/sharp-eels-study.md new file mode 100644 index 00000000000..54d2b99f904 --- /dev/null +++ b/.changeset/sharp-eels-study.md @@ -0,0 +1,7 @@ +--- +'@primer/react': minor +--- + +Adds onSelectSuggestion callback to + + From 9f4fc22c9e482cf8aef0ff937af13ed7b7a34d7d Mon Sep 17 00:00:00 2001 From: Patrick Dinger <121539+paxos@users.noreply.github.com> Date: Fri, 18 Aug 2023 15:23:00 -0700 Subject: [PATCH 05/12] Add storyboard --- .../InlineAutocomplete.features.stories.tsx | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/drafts/InlineAutocomplete/InlineAutocomplete.features.stories.tsx b/src/drafts/InlineAutocomplete/InlineAutocomplete.features.stories.tsx index 3f2f9c5a6ae..96ae45e30d1 100644 --- a/src/drafts/InlineAutocomplete/InlineAutocomplete.features.stories.tsx +++ b/src/drafts/InlineAutocomplete/InlineAutocomplete.features.stories.tsx @@ -56,6 +56,36 @@ export const SingleLine = ({loading, tabInserts}: ArgProps) => { ) } +export const OnSelectSuggestion = ({loading, tabInserts}: ArgProps) => { + const [suggestions, setSuggestions] = useState(null) + + const onShowSuggestions = (event: ShowSuggestionsEvent) => { + if (loading) { + setSuggestions('loading') + return + } + + setSuggestions(filteredUsers(event.query).map(user => user.login)) + } + + return ( + + Inline Autocomplete Demo + Try typing '@' to show user suggestions. + window.alert(`Selected ${suggestion}`)} + suggestions={suggestions} + onShowSuggestions={onShowSuggestions} + onHideSuggestions={() => setSuggestions(null)} + tabInsertsSuggestions={tabInserts} + > + + + + ) +} + const UserSuggestion = ({user, ...props}: {user: User} & ActionListItemProps) => ( From 237ea313b23cb5bece4d174c29f0a23563a8fae6 Mon Sep 17 00:00:00 2001 From: Patrick Dinger <121539+paxos@users.noreply.github.com> Date: Mon, 21 Aug 2023 08:57:28 -0700 Subject: [PATCH 06/12] Update src/drafts/InlineAutocomplete/InlineAutocomplete.tsx Co-authored-by: Ian Sanders --- src/drafts/InlineAutocomplete/InlineAutocomplete.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/drafts/InlineAutocomplete/InlineAutocomplete.tsx b/src/drafts/InlineAutocomplete/InlineAutocomplete.tsx index bf3048f539b..738ede02959 100644 --- a/src/drafts/InlineAutocomplete/InlineAutocomplete.tsx +++ b/src/drafts/InlineAutocomplete/InlineAutocomplete.tsx @@ -169,9 +169,7 @@ const InlineAutocomplete = ({ if (!inputRef.current || !showEventRef.current) return const {query, trigger} = showEventRef.current - if (onSelectSuggestion) { - onSelectSuggestion(suggestion) - } + onSelectSuggestion?.(suggestion) const currentCaretPosition = getSelectionStart(inputRef.current) ?? 0 const deleteLength = query.length + trigger.triggerChar.length From 5d1b9fadbecb7c3f7deb64cb812cecfa0999886b Mon Sep 17 00:00:00 2001 From: Patrick Dinger <121539+paxos@users.noreply.github.com> Date: Tue, 22 Aug 2023 12:42:29 -0700 Subject: [PATCH 07/12] Introduce SelectSuggestionsEvent --- src/drafts/InlineAutocomplete/InlineAutocomplete.tsx | 5 +++-- src/drafts/InlineAutocomplete/types.ts | 5 +++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/drafts/InlineAutocomplete/InlineAutocomplete.tsx b/src/drafts/InlineAutocomplete/InlineAutocomplete.tsx index 738ede02959..8530e825809 100644 --- a/src/drafts/InlineAutocomplete/InlineAutocomplete.tsx +++ b/src/drafts/InlineAutocomplete/InlineAutocomplete.tsx @@ -6,6 +6,7 @@ import {useSyntheticChange} from '../hooks/useSyntheticChange' import {getAbsoluteCharacterCoordinates} from '../utils/character-coordinates' import { + SelectSuggestionsEvent, ShowSuggestionsEvent, Suggestions, SuggestionsPlacement, @@ -29,7 +30,7 @@ export type InlineAutocompleteProps = { /** Called when a suggestion is selected. */ - onSelectSuggestion?: (suggestion: string) => void + onSelectSuggestion?: (event: SelectSuggestionsEvent) => void /** Called when suggestions should be hidden. Set `suggestions` to `null` in this case. */ onHideSuggestions: () => void @@ -169,7 +170,7 @@ const InlineAutocomplete = ({ if (!inputRef.current || !showEventRef.current) return const {query, trigger} = showEventRef.current - onSelectSuggestion?.(suggestion) + onSelectSuggestion?.({suggestion, trigger, query}) const currentCaretPosition = getSelectionStart(inputRef.current) ?? 0 const deleteLength = query.length + trigger.triggerChar.length diff --git a/src/drafts/InlineAutocomplete/types.ts b/src/drafts/InlineAutocomplete/types.ts index 2c4d22fb4f9..2b7334d1c26 100644 --- a/src/drafts/InlineAutocomplete/types.ts +++ b/src/drafts/InlineAutocomplete/types.ts @@ -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 From 69be83f3a4f1d8345aeca2505f9dfec0545da7a3 Mon Sep 17 00:00:00 2001 From: Patrick Dinger <121539+paxos@users.noreply.github.com> Date: Tue, 22 Aug 2023 12:58:05 -0700 Subject: [PATCH 08/12] Update src/drafts/InlineAutocomplete/InlineAutocomplete.tsx Co-authored-by: Ian Sanders --- src/drafts/InlineAutocomplete/InlineAutocomplete.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/drafts/InlineAutocomplete/InlineAutocomplete.tsx b/src/drafts/InlineAutocomplete/InlineAutocomplete.tsx index 8530e825809..f67f6bf80a0 100644 --- a/src/drafts/InlineAutocomplete/InlineAutocomplete.tsx +++ b/src/drafts/InlineAutocomplete/InlineAutocomplete.tsx @@ -28,7 +28,12 @@ export type InlineAutocompleteProps = { */ onShowSuggestions: (event: ShowSuggestionsEvent) => void - /** Called when a suggestion is selected. + /** + * 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 From 57298af361a8cb60edd87af4d354d69fea560829 Mon Sep 17 00:00:00 2001 From: Patrick Dinger <121539+paxos@users.noreply.github.com> Date: Thu, 24 Aug 2023 10:37:03 -0700 Subject: [PATCH 09/12] Update InlineAutocomplete.docs.json --- src/drafts/InlineAutocomplete/InlineAutocomplete.docs.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/drafts/InlineAutocomplete/InlineAutocomplete.docs.json b/src/drafts/InlineAutocomplete/InlineAutocomplete.docs.json index e4d8c06dabd..a8dbca5795d 100644 --- a/src/drafts/InlineAutocomplete/InlineAutocomplete.docs.json +++ b/src/drafts/InlineAutocomplete/InlineAutocomplete.docs.json @@ -19,6 +19,12 @@ "required": true, "description": "Register the triggers that can cause suggestions to appear." }, + { + "name": "onSelectSuggestion?.({suggestion, trigger, query})", + "type": "({suggestion, trigger, query}) => void", + "defaultValue": null, + "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", From da2a2204d89aff68b1b296f6df828a4d67e61157 Mon Sep 17 00:00:00 2001 From: Patrick Dinger <121539+paxos@users.noreply.github.com> Date: Thu, 24 Aug 2023 11:25:47 -0700 Subject: [PATCH 10/12] Update docs --- src/drafts/InlineAutocomplete/InlineAutocomplete.docs.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drafts/InlineAutocomplete/InlineAutocomplete.docs.json b/src/drafts/InlineAutocomplete/InlineAutocomplete.docs.json index a8dbca5795d..78f1d48ecc5 100644 --- a/src/drafts/InlineAutocomplete/InlineAutocomplete.docs.json +++ b/src/drafts/InlineAutocomplete/InlineAutocomplete.docs.json @@ -20,7 +20,7 @@ "description": "Register the triggers that can cause suggestions to appear." }, { - "name": "onSelectSuggestion?.({suggestion, trigger, query})", + "name": "onSelectSuggestion", "type": "({suggestion, trigger, query}) => void", "defaultValue": null, "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." From 9ea2d751b3c9ea599cb0536300f6120752059888 Mon Sep 17 00:00:00 2001 From: Patrick Dinger <121539+paxos@users.noreply.github.com> Date: Thu, 24 Aug 2023 12:10:21 -0700 Subject: [PATCH 11/12] Update docs --- src/drafts/InlineAutocomplete/InlineAutocomplete.docs.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drafts/InlineAutocomplete/InlineAutocomplete.docs.json b/src/drafts/InlineAutocomplete/InlineAutocomplete.docs.json index 78f1d48ecc5..2bc5f404d83 100644 --- a/src/drafts/InlineAutocomplete/InlineAutocomplete.docs.json +++ b/src/drafts/InlineAutocomplete/InlineAutocomplete.docs.json @@ -21,7 +21,7 @@ }, { "name": "onSelectSuggestion", - "type": "({suggestion, trigger, query}) => void", + "type": "(event: SelectSuggestionsEvent) => void", "defaultValue": null, "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." }, From 3daa406985497373281d1290bdcfc507268fe254 Mon Sep 17 00:00:00 2001 From: Patrick Dinger <121539+paxos@users.noreply.github.com> Date: Thu, 24 Aug 2023 12:20:32 -0700 Subject: [PATCH 12/12] Update docs --- src/drafts/InlineAutocomplete/InlineAutocomplete.docs.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drafts/InlineAutocomplete/InlineAutocomplete.docs.json b/src/drafts/InlineAutocomplete/InlineAutocomplete.docs.json index 2bc5f404d83..eaded29db28 100644 --- a/src/drafts/InlineAutocomplete/InlineAutocomplete.docs.json +++ b/src/drafts/InlineAutocomplete/InlineAutocomplete.docs.json @@ -22,7 +22,7 @@ { "name": "onSelectSuggestion", "type": "(event: SelectSuggestionsEvent) => void", - "defaultValue": null, + "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." }, {