Skip to content

[BpkChatbotInput]Bpk component chatbox input#4279

Merged
GC Zhu (gc-skyscanner) merged 31 commits intomainfrom
bpk-component-chatbox-input
Mar 24, 2026
Merged

[BpkChatbotInput]Bpk component chatbox input#4279
GC Zhu (gc-skyscanner) merged 31 commits intomainfrom
bpk-component-chatbox-input

Conversation

@gc-skyscanner
Copy link
Copy Markdown
Contributor

@gc-skyscanner GC Zhu (gc-skyscanner) commented Mar 13, 2026

Summary

Migrate BpkChatbotInput from carhire-homepage into Backpack and promote it as an official component.

Differences with carhire-hompage(the original implementation)

Input type API

  • Renamed types for clarity: DEFAULTCARS, COMPOSORCARS_COMPOSER, added variant
    COMPOSER
  • COMPOSER is the default inputType
  • Renamed prop inputPlaceholderplaceholder

Input types

CHATBOT_INPUT_TYPES.CARS uses a single-line <input> with a featured send button. CHATBOT_INPUT_TYPES.COMPOSER and CHATBOT_INPUT_TYPES.CARS_COMPOSER both use a multi-line <textarea> with a primary send button, but differ in the following ways:

Feature COMPOSER CARS_COMPOSER
Placeholder colour $bpk-text-primary-day $bpk-text-secondary-day
Placeholder hidden on focus Yes No
Box shadow on focus Yes No

Theming

  • Added BpkThemeProvider support via chatbotInputBorderRadius and chatbotInputFocusBorderColor

SCSS improvements

  • Replaced raw :hover with @include utils.bpk-hover (respects .bpk-no-touch-support)
  • Replaced hardcoded 0.2s transition duration with tokens.$bpk-duration-sm
  • Removed dead / commented-out code

Internal refactoring

  • Extracted state logic into dedicated hooks: useChatbotInput, useChatbotInputManager, useInputHandlers, useTextAreaAutoResize
  • Used useId() for accessible label/input ID pairing
  • Moved shared types into common-types.ts

Bug fixes

  • isPolling state now correctly blocks submission
  • Keyboard events no longer silently swallowed (e.g. Escape can now close a parent modal)

Screenshots

composer

Screenshots figma
image image

cars

Screenshots figma
image image

cars-composer

Screenshots figma
image image

Remember to include the following changes:

  • Ensure the PR title includes the name of the component you are changing so it's clear in the release notes for consumers of the changes in the version e.g [Clover-123][BpkButton] Updating the colour
  • README.md (If you have created a new component)
  • Component README.md
  • Tests
  • Accessibility tests
    • The following checks were performed:
      • Ability to navigate using a keyboard only
      • Zoom functionality (Deque University explanation):
        • The page SHOULD be functional AND readable when only the text is magnified to 200% of its initial size
        • Pages must reflow as zoom increases up to 400% so that content continues to be presented in only one column i.e. Content MUST NOT require scrolling in two directions (both vertically and horizontally)
      • Ability to navigate using a screen reader only
  • Storybook examples created/updated
  • For breaking changes or deprecating components/properties, migration guides added to the description of the PR. If the guide has large changes, consider creating a new Markdown page inside the component's docs folder and link it here

GC Zhu (gc-skyscanner) and others added 14 commits March 9, 2026 18:29
…operty (#4265)

* add Claude SDD spec for BpkButton

* add notes for the definition of themeable

* Make corner radius configurable via bpk-themeable

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update specs/001-bpkbutton-baseline/spec.md

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* solve comment

* [CLOV-1327] Address Copilot review comments

- Fix Object.keys → Object.values in BpkButton-test.tsx forEach loop so
  tests iterate over kebab-case type values (e.g. primary-on-dark) that
  match actual BEM class names, not camelCase keys
- Remove unused ButtonType import (no longer needed after removing cast)
- Update plan.md: replace "snapshot regeneration" with accurate description
  of explicit assertion approach; expand Files changed list to include test,
  snapshot deletion, and spec documentation artefacts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* remove the BpkButton spec

* add storybook example for the themed boder radius for BpkButton

* imporve BpkButton unit test

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Bumps the artifacts-actions group with 1 update: [actions/download-artifact](https://github.com/actions/download-artifact).


Updates `actions/download-artifact` from 7.0.0 to 8.0.0
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](actions/download-artifact@37930b1...70fc10c)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: 8.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: artifacts-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Copilot AI review requested due to automatic review settings March 13, 2026 08:52
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a new bpk-component-chatbot-input package to Backpack, providing a chat-style input with a default single-line mode and a composer (textarea) mode, plus supporting hooks, styling, tests, and Storybook examples.

Changes:

  • Introduces BpkChatbotInput with default + composer modes and a send button/loading state.
  • Adds supporting hooks (useChatbotInput, useChatbotInputManager, useTextAreaAutoResize, useInputHandlers) and component subparts (InputField, TextAreaField, SendButton).
  • Adds unit tests, accessibility tests, snapshots, and Storybook examples for the new component.

Reviewed changes

Copilot reviewed 31 out of 31 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
packages/bpk-component-chatbot-input/src/types.ts Defines shared prop types for input field variants.
packages/bpk-component-chatbot-input/src/hooks/useTextAreaAutoResize.ts Implements textarea measurement + auto-resize logic.
packages/bpk-component-chatbot-input/src/hooks/useTextAreaAutoResize-test.tsx Tests textarea auto-resize behavior and scrolling behavior.
packages/bpk-component-chatbot-input/src/hooks/useInputHandlers.ts Centralizes click/touch/change handlers for input controls.
packages/bpk-component-chatbot-input/src/hooks/useInputHandlers-test.tsx Tests handler behavior for click/touch/change.
packages/bpk-component-chatbot-input/src/hooks/useChatbotInputManager.ts Adds state management for chatbot input value and submit/autoclear behavior.
packages/bpk-component-chatbot-input/src/hooks/useChatbotInputManager-test.tsx Tests input manager state transitions and submission logic.
packages/bpk-component-chatbot-input/src/hooks/useChatbotInput.ts Provides UI state (focused/disabled/overlimit/etc) and input props for the component.
packages/bpk-component-chatbot-input/src/hooks/useChatbotInput-test.tsx Tests derived state and key-handling behavior for the hook.
packages/bpk-component-chatbot-input/src/hooks/index.ts Barrel exports for hooks used by the component.
packages/bpk-component-chatbot-input/src/constants.ts Declares input type constants and default max character limit.
packages/bpk-component-chatbot-input/src/accessibility-test.tsx Adds jest-axe coverage for default/composer/loading renders.
packages/bpk-component-chatbot-input/src/snapshots/BpkChatbotInput-test.tsx.snap Stores render snapshots for default and composer variants.
packages/bpk-component-chatbot-input/src/TextAreaField/TextAreaField.tsx Implements the composer textarea UI.
packages/bpk-component-chatbot-input/src/TextAreaField/TextAreaField.module.scss Styles for composer textarea container/field.
packages/bpk-component-chatbot-input/src/TextAreaField/TextAreaField-test.tsx Tests textarea field rendering, events, and axe checks.
packages/bpk-component-chatbot-input/src/SendButton/SendButton.tsx Adds send button with icon + loading state behavior.
packages/bpk-component-chatbot-input/src/SendButton/SendButton-test.tsx Tests send button render/click/disabled + axe.
packages/bpk-component-chatbot-input/src/LoadingButton/LoadingButton.tsx Adds a loading button implementation (currently not referenced).
packages/bpk-component-chatbot-input/src/LoadingButton/LoadingButton.module.scss Styles for the loading button.
packages/bpk-component-chatbot-input/src/InputField/InputField.tsx Implements default single-line input UI.
packages/bpk-component-chatbot-input/src/InputField/InputField.module.scss Styles for the single-line input field.
packages/bpk-component-chatbot-input/src/InputField/InputField-test.tsx Tests input field rendering, events, and axe checks.
packages/bpk-component-chatbot-input/src/BpkChatbotInput.tsx Main component wiring modes, hooks, and send button.
packages/bpk-component-chatbot-input/src/BpkChatbotInput.module.scss Styles for default/composer container variants.
packages/bpk-component-chatbot-input/src/BpkChatbotInput-test.tsx Component tests for variants, loading, and keyboard interactions.
packages/bpk-component-chatbot-input/index.ts Public entrypoint exports for the new package.
packages/bpk-component-chatbot-input/README.md Package documentation and usage instructions.
examples/bpk-component-chatbot-input/stories.tsx Storybook story registration for the new component.
examples/bpk-component-chatbot-input/examples.tsx Interactive examples for default/composer/loading states.
examples/bpk-component-chatbot-input/examples.module.scss Example layout styling.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +35 to +41
export const LINE_HEIGHT = 24;
export const MIN_INPUT_HEIGHT = LINE_HEIGHT;
export const MAX_INPUT_HEIGHT_PHASE_1 = LINE_HEIGHT * 4;
export const MAX_INPUT_HEIGHT_PHASE_2 = LINE_HEIGHT * 5;
export const MIN_CONTAINER_HEIGHT = 24;
export const MAX_CONTAINER_HEIGHT = 96;
export const PARENT_PADDING_TOP = 16;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Comment thread packages/bpk-component-chatbot-input/src/InputField/InputField.tsx
Comment thread packages/bpk-component-chatbot-input/src/LoadingButton/LoadingButton.tsx Outdated
Comment thread packages/bpk-component-chatbot-input/src/BpkChatbotInput.tsx
Comment thread packages/bpk-component-chatbot-input/src/hooks/useInputHandlers.ts Outdated
Comment thread packages/bpk-component-chatbot-input/src/hooks/useChatbotInput.ts Outdated
Comment thread packages/bpk-component-chatbot-input/src/TextAreaField/TextAreaField.tsx Outdated
@skyscanner-backpack-bot
Copy link
Copy Markdown

Visit https://backpack.github.io/storybook-prs/4279 to see this build running in a browser.

@skyscanner-backpack-bot
Copy link
Copy Markdown

skyscanner-backpack-bot Bot commented Mar 13, 2026

Browser support

If this is a visual change, make sure you've tested it in multiple browsers.

Generated by 🚫 dangerJS against e2c4666

@skyscanner-backpack-bot
Copy link
Copy Markdown

Visit https://backpack.github.io/storybook-prs/4279 to see this build running in a browser.

@skyscanner-backpack-bot
Copy link
Copy Markdown

Visit https://backpack.github.io/storybook-prs/4279 to see this build running in a browser.

@skyscanner-backpack-bot
Copy link
Copy Markdown

Visit https://backpack.github.io/storybook-prs/4279 to see this build running in a browser.

@skyscanner-backpack-bot
Copy link
Copy Markdown

Visit https://backpack.github.io/storybook-prs/4279 to see this build running in a browser.

Comment on lines +53 to +56
const measureElementRef = useRef<HTMLTextAreaElement | null>(null);
const previousValueRef = useRef<string>('');
const shouldScrollRef = useRef<boolean>(false);
const isInitialRenderRef = useRef<boolean>(true);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Why useRef for all four?(same implementation with carhire's)
All four values share one thing in common: changes to them don't need to trigger a UI re-render.

ref reason
measureElementRef Stores a DOM node — the classic use case for useRef
previousValueRef A pure computation helper, only read/written inside effects, never affects render output
shouldScrollRef A cross-effect signal flag — using useState would cause unnecessary re-renders or even an infinite loop
isInitialRenderRef A one-time flag that flips once over the component's lifetime, has no impact on any UI

Of the values in this hook, only dimensions uses useState — because those values are applied directly as inline styles and must trigger a re-render to take effect visually.

@gc-skyscanner GC Zhu (gc-skyscanner) force-pushed the bpk-component-chatbox-input branch from 7090976 to 0ce94e4 Compare March 17, 2026 11:30
@skyscanner-backpack-bot
Copy link
Copy Markdown

Visit https://backpack.github.io/storybook-prs/4279 to see this build running in a browser.

@skyscanner-backpack-bot
Copy link
Copy Markdown

Visit https://backpack.github.io/storybook-prs/4279 to see this build running in a browser.

@skyscanner-backpack-bot
Copy link
Copy Markdown

Visit https://backpack.github.io/storybook-prs/4279 to see this build running in a browser.

@skyscanner-backpack-bot
Copy link
Copy Markdown

Visit https://backpack.github.io/storybook-prs/4279 to see this build running in a browser.

@skyscanner-backpack-bot
Copy link
Copy Markdown

Visit https://backpack.github.io/storybook-prs/4279 to see this build running in a browser.

if (e.key === 'Enter') {
if (!isMultiLine) {
e.preventDefault();
e.stopPropagation();
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@skyscanner-backpack-bot
Copy link
Copy Markdown

Visit https://backpack.github.io/storybook-prs/4279 to see this build running in a browser.

@IrinaWei
Copy link
Copy Markdown
Contributor

I have to click on the input box to see the rest of the content in this case 🤔
image

@gc-skyscanner
Copy link
Copy Markdown
Contributor Author

#4279 (comment)
Good catch, and it is same as the original implementation with Cars.
After discussion, we decided to keep it as for the moment, and create a ticket to optimize it in the future.

@skyscanner-backpack-bot
Copy link
Copy Markdown

Visit https://backpack.github.io/storybook-prs/4279 to see this build running in a browser.

position: relative;
transition:
transform 0.3s cubic-bezier(0.4, 0, 0.2, 1),
box-shadow 0.3s cubic-bezier(0.4, 0, 0.2, 1);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit: Why composer variant use hard code ,but cars variant use box-shadow tokens.$bpk-duration-sm ease;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

similar here:
keep it same as carhires'
#4279 (comment)

);
```

## Props
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

As Storybook already shows the props, we don’t need to document the props again in the README.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

good catch! update in e2c4666

@skyscanner-backpack-bot
Copy link
Copy Markdown

Visit https://backpack.github.io/storybook-prs/4279 to see this build running in a browser.

@kerrie-wu
Copy link
Copy Markdown
Contributor

Just a query regarding over 500 cases, there’s no character counter or over-limit message, so users are unaware of why Send is unavailable.
image

@kerrie-wu
Copy link
Copy Markdown
Contributor

@gc-skyscanner
Copy link
Copy Markdown
Contributor Author

#4279 (comment)

Yeah, you're right, here is no error/warning message there 😢 but it is same as cars behaviour and figma

cars Figma
image image

@gc-skyscanner
Copy link
Copy Markdown
Contributor Author

#4279 (comment)

it should be grey border when unfocused/ un hover( To see this in cars’ storybook) keep the same behaviour with cars because it is the cars-specific variant for cars, it does have blue border when hover)
Cars:

unfocus hover
image image

Copy link
Copy Markdown
Contributor

@kerrie-wu kerrie-wu left a comment

Choose a reason for hiding this comment

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

LGTM

@gc-skyscanner GC Zhu (gc-skyscanner) merged commit f8f06c0 into main Mar 24, 2026
15 checks passed
@gc-skyscanner GC Zhu (gc-skyscanner) deleted the bpk-component-chatbox-input branch March 24, 2026 02:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai: claude minor Non breaking change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants