From 77e7ab057bef5394e757356c58ca7ae0f0bfdb64 Mon Sep 17 00:00:00 2001 From: Ian Sanders Date: Tue, 2 Aug 2022 11:39:25 -0400 Subject: [PATCH] Add `InlineAutocomplete` component (#2157) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add `useCombobox` hook, extending `@github/combobox-nav` * Add `useSyntheticChange` hook * Add `InlineAutocomplete` component * Refactor and improve comments * Remove extra type * Add story and make it work with `FormControl` * Add to main exports * Add MDX file * Remove unecessary ID on textarea in story * Remove version-lock from new dependencies * Make type of render function more specific * Add unit tests * Simplify `useCombobox` and use `navigate` to focus first item Fixes not having an `aria-activedescendant` initially defined * Fix tests by wrapping `userEvent.type` in `act` * Fix preventing blur when tabbing from loading state * Delete unused imports * Change interfaces out for object types * Add accessible live status message to describe suggestions * Dynamically assign the combobox role to avoid treating the textarea as a combobox when no suggestions are available * Shorten & revise status message * Move to drafts * Move docs to drafts * Fix import in docs * Update combobox-nav dependency * Add option to control whether `Tab` key inserts suggestions * Style the defaulted-to first option differently from the selected option * Update combobox-nav dependency * Update and fix unit tests * Remove unused import (fix lint error) * docs: add drafts metastring * Remove `selectionVariant` from suggestions list * Add `install:docs` script * Add more examples to docs * Add more stories * Fix _another_ bug with the caret-coordinates utility and single-line inputs 🙃 * Move component & hooks to drafts folder * Move stories & tests into drafts * Remove non-null assertions in tests * Move `textarea-caret` type declaration to `@types` * Add props table * Fix TS issue * Create cuddly-bags-sort.md Co-authored-by: Siddharth Kshetrapal --- .changeset/cuddly-bags-sort.md | 5 + @types/@koddsson/index.d.ts | 0 @types/@koddsson/textarea-caret/index.d.ts | 11 + docs/content/drafts/InlineAutocomplete.mdx | 162 ++++++ docs/package-lock.json | 105 ++-- .../src/@primer/gatsby-theme-doctocat/nav.yml | 2 + jest.config.js | 3 +- package-lock.json | 22 + package.json | 3 + src/FormControl/FormControl.tsx | 12 +- .../InlineAutocomplete.stories.tsx | 174 +++++++ .../InlineAutocomplete.test.tsx | 470 ++++++++++++++++++ .../InlineAutocomplete/InlineAutocomplete.tsx | 220 ++++++++ .../_AutocompleteSuggestions.tsx | 119 +++++ src/drafts/InlineAutocomplete/index.ts | 6 + src/drafts/InlineAutocomplete/types.ts | 56 +++ src/drafts/InlineAutocomplete/utils.ts | 160 ++++++ src/drafts/hooks/useCombobox.ts | 153 ++++++ src/drafts/hooks/useSyntheticChange.ts | 149 ++++++ src/drafts/index.ts | 9 + 20 files changed, 1769 insertions(+), 72 deletions(-) create mode 100644 .changeset/cuddly-bags-sort.md create mode 100644 @types/@koddsson/index.d.ts create mode 100644 @types/@koddsson/textarea-caret/index.d.ts create mode 100644 docs/content/drafts/InlineAutocomplete.mdx create mode 100644 src/drafts/InlineAutocomplete/InlineAutocomplete.stories.tsx create mode 100644 src/drafts/InlineAutocomplete/InlineAutocomplete.test.tsx create mode 100644 src/drafts/InlineAutocomplete/InlineAutocomplete.tsx create mode 100644 src/drafts/InlineAutocomplete/_AutocompleteSuggestions.tsx create mode 100644 src/drafts/InlineAutocomplete/index.ts create mode 100644 src/drafts/InlineAutocomplete/types.ts create mode 100644 src/drafts/InlineAutocomplete/utils.ts create mode 100644 src/drafts/hooks/useCombobox.ts create mode 100644 src/drafts/hooks/useSyntheticChange.ts diff --git a/.changeset/cuddly-bags-sort.md b/.changeset/cuddly-bags-sort.md new file mode 100644 index 00000000000..d9ae0c3c250 --- /dev/null +++ b/.changeset/cuddly-bags-sort.md @@ -0,0 +1,5 @@ +--- +"@primer/react": patch +--- + +Add `InlineAutocomplete` component, `useCombobox` hook, and `useSyntheticChange` hook to drafts diff --git a/@types/@koddsson/index.d.ts b/@types/@koddsson/index.d.ts new file mode 100644 index 00000000000..e69de29bb2d diff --git a/@types/@koddsson/textarea-caret/index.d.ts b/@types/@koddsson/textarea-caret/index.d.ts new file mode 100644 index 00000000000..c2505997701 --- /dev/null +++ b/@types/@koddsson/textarea-caret/index.d.ts @@ -0,0 +1,11 @@ +declare module '@koddsson/textarea-caret' { + export interface CaretCoordinates { + top: number + left: number + height: number + } + export default function getCaretCoordinates( + input: HTMLTextAreaElement | HTMLInputElement, + index: number + ): CaretCoordinates +} diff --git a/docs/content/drafts/InlineAutocomplete.mdx b/docs/content/drafts/InlineAutocomplete.mdx new file mode 100644 index 00000000000..32fa20c9dcf --- /dev/null +++ b/docs/content/drafts/InlineAutocomplete.mdx @@ -0,0 +1,162 @@ +--- +title: InlineAutocomplete +componentId: inline_autocomplete +status: Draft +description: Provides inline auto completion suggestions for an input or textarea. +source: https://github.com/primer/react/tree/main/src/InlineAutocomplete +storybook: '/react/storybook?path=/story/forms-inlineautocomplete--default' +--- + +```js +import {InlineAutocomplete} from '@primer/react/drafts' +``` + +The `InlineAutocomplete` component extends an `Input` or `Textarea` component to provide inline suggestions, similar to those provided by a code editor. + +## Examples + + + +Input components **must always** be accompanied by a corresponding label to improve support for assistive +technologies. Examples below are provided for conciseness and may not reflect accessibility best practices. + +`InlineAutocomplete` can be used with the [`FormControl`](/FormControl) component to render a corresponding label. + + + +### Multi-line input + +Try typing a `#` symbol to see suggestions. Use `Enter` or click to apply a suggestion. + +```javascript live noinline drafts +const options = ['javascript', 'typescript', 'css', 'html', 'webassembly'] + +const SimpleExample = () => { + const [suggestions, setSuggestions] = React.useState([]) + + return ( + setSuggestions(options.filter(tag => tag.includes(query)))} + onHideSuggestions={() => setSuggestions([])} + > +