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

WIP: bpk autosuggest fires change event on value change #3611

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions examples/bpk-component-autosuggest/stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { ArgsTable } from '@storybook/addon-docs';
import { Title, Markdown, PRIMARY_STORY } from '@storybook/blocks';
import { userEvent, within } from '@storybook/testing-library';

import BpkAutosuggest from '../../packages/bpk-component-autosuggest/src/BpkAutosuggest';
import AutosuggestWrapper from '../../packages/bpk-component-autosuggest/src/BpkAutosuggest';
import BpkAutosuggestSuggestion from '../../packages/bpk-component-autosuggest/src/BpkAutosuggestSuggestion';

import AutosuggestExample from './examples';
Expand All @@ -29,7 +29,7 @@ import type { StoryObj } from '@storybook/react';

export default {
title: 'bpk-component-autosuggest',
component: BpkAutosuggest,
component: AutosuggestWrapper,
subcomponents: {
BpkAutosuggestSuggestion,
},
Expand All @@ -41,7 +41,7 @@ export default {
<ArgsTable of={PRIMARY_STORY} />
<Markdown>
{
`**BpkAutosuggest:**
`**AutosuggestWrapper:**
Please refer to react-autosuggest's
documentation for a full list of [props](https://github.com/moroshko/react-autosuggest#props).
**Note:** The \`inputProps\` object is passed directly to a
Expand Down Expand Up @@ -87,7 +87,7 @@ export const VisualTest: Story = {
render: () => <AutosuggestExample alwaysRenderSuggestions />,
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);

const input = canvas.getByPlaceholderText('Enter an office name'); // Find the input field
input.focus(); // Explicitly set focus using the DOM's focus method
await userEvent.type(input, 'Lon', { delay: 100 }); // Simulate typing into the input field
Expand All @@ -102,4 +102,4 @@ export const VisualTest: Story = {
waitForSelector: '.percy-selector-placeholder', // Wait for the input to have this class before taking the snapshot
}
}
};
};
4 changes: 2 additions & 2 deletions packages/bpk-component-autosuggest/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@

/* @flow strict */

import BpkAutosuggest from './src/BpkAutosuggest';
import AutosuggestWrapper from './src/BpkAutosuggest';
import BpkAutosuggestSuggestion from './src/BpkAutosuggestSuggestion';

export default BpkAutosuggest;
export default AutosuggestWrapper;
export { BpkAutosuggestSuggestion };
14 changes: 7 additions & 7 deletions packages/bpk-component-autosuggest/src/BpkAutosuggest-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

import { render } from '@testing-library/react';

import BpkAutosuggest from './BpkAutosuggest';
import AutosuggestWrapper from './BpkAutosuggest';

const suggestions = ['Edinburgh', 'Glasgow', 'London'];
const onSuggestionsFetchRequested = () => null;
Expand All @@ -34,10 +34,10 @@ const inputProps = {
onChange: () => null,
};

describe('BpkAutosuggest', () => {
describe('AutosuggestWrapper', () => {
it('should render correctly', () => {
const { asFragment } = render(
<BpkAutosuggest
<AutosuggestWrapper
suggestions={suggestions}
onSuggestionsFetchRequested={onSuggestionsFetchRequested}
onSuggestionsClearRequested={onSuggestionsClearRequested}
Expand All @@ -51,7 +51,7 @@ describe('BpkAutosuggest', () => {

it('should render correctly with an "alwaysRenderSuggestions" attribute', () => {
const { asFragment } = render(
<BpkAutosuggest
<AutosuggestWrapper
suggestions={suggestions}
onSuggestionsFetchRequested={onSuggestionsFetchRequested}
onSuggestionsClearRequested={onSuggestionsClearRequested}
Expand All @@ -72,7 +72,7 @@ describe('BpkAutosuggest', () => {
};

const { container } = render(
<BpkAutosuggest
<AutosuggestWrapper
suggestions={suggestions}
onSuggestionsFetchRequested={onSuggestionsFetchRequested}
onSuggestionsClearRequested={onSuggestionsClearRequested}
Expand All @@ -88,7 +88,7 @@ describe('BpkAutosuggest', () => {

it('should default autocomplete to off', () => {
const { container } = render(
<BpkAutosuggest
<AutosuggestWrapper
suggestions={suggestions}
onSuggestionsFetchRequested={onSuggestionsFetchRequested}
onSuggestionsClearRequested={onSuggestionsClearRequested}
Expand All @@ -104,7 +104,7 @@ describe('BpkAutosuggest', () => {

it('should allow a consumer to override autocomplete', () => {
const { container } = render(
<BpkAutosuggest
<AutosuggestWrapper
suggestions={suggestions}
onSuggestionsFetchRequested={onSuggestionsFetchRequested}
onSuggestionsClearRequested={onSuggestionsClearRequested}
Expand Down
27 changes: 25 additions & 2 deletions packages/bpk-component-autosuggest/src/BpkAutosuggest.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@

/* @flow strict */

import { useState } from 'react';

import Autosuggest from 'react-autosuggest';

import BpkInput from '../../bpk-component-input';
import { cssModules } from '../../bpk-react-utils';
import { cssModules, setNativeValue } from '../../bpk-react-utils';

import STYLES from './BpkAutosuggest.module.scss';

Expand Down Expand Up @@ -75,4 +77,25 @@ Autosuggest.defaultProps.renderInputComponent = (inputProps: Props) => {
);
};

export default Autosuggest;
const AutosuggestWrapper = (props) => {
// eslint-disable-next-line react/prop-types
const [value, setValue] = useState(props.inputProps.value || '');
const newProps = props;
// eslint-disable-next-line react/prop-types
const { onBlur } = props.inputProps;
newProps.inputProps.onBlur = (event) => {
if (value !== event.target.value) {
// We want change events to be sent when a user has clicked on a suggestion
setNativeValue(event.target, event.target.value);
if (onBlur) {
onBlur(event);
}
setValue(event.target.value);
}
};
return (
<Autosuggest {...newProps} />
)};


export default AutosuggestWrapper;
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`BpkAutosuggest should render correctly 1`] = `
exports[`AutosuggestWrapper should render correctly 1`] = `
<DocumentFragment>
<div
aria-expanded="false"
Expand Down Expand Up @@ -29,7 +29,7 @@ exports[`BpkAutosuggest should render correctly 1`] = `
</DocumentFragment>
`;

exports[`BpkAutosuggest should render correctly with an "alwaysRenderSuggestions" attribute 1`] = `
exports[`AutosuggestWrapper should render correctly with an "alwaysRenderSuggestions" attribute 1`] = `
<DocumentFragment>
<div
aria-expanded="true"
Expand Down
4 changes: 2 additions & 2 deletions packages/bpk-component-autosuggest/src/accessibility-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import { render } from '@testing-library/react';
import { axe } from 'jest-axe';

import BpkAutosuggest from './BpkAutosuggest';
import AutosuggestWrapper from './BpkAutosuggest';

const suggestions = ['Edinburgh', 'Glasgow', 'London'];
const onSuggestionsFetchRequested = () => null;
Expand All @@ -43,7 +43,7 @@ describe('BpkAutosuggest accessibility tests', () => {
*/
it.skip('should not have programmatically-detectable accessibility issues', async () => {
const { container } = render(
<BpkAutosuggest
<AutosuggestWrapper
suggestions={suggestions}
onSuggestionsFetchRequested={onSuggestionsFetchRequested}
onSuggestionsClearRequested={onSuggestionsClearRequested}
Expand Down
113 changes: 113 additions & 0 deletions packages/bpk-component-autosuggest/src/form-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* Backpack - Skyscanner's Design System
*
* Copyright 2016 Skyscanner Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/* @flow strict */

import { useEffect, useState } from 'react';

import { render, screen, fireEvent } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

import AutosuggestWrapper from './BpkAutosuggest';

const suggestions = ['Edinburgh', 'Glasgow', 'London'];
const onSuggestionsFetchRequested = () => null;
const onSuggestionsClearRequested = () => null;
const getSuggestionValue = (suggestion) => suggestion;
const renderSuggestion = (suggestion) => <span>{suggestion}</span>;
const inputProps = {
id: 'origin',
name: 'value',
value: '',
onChange: jest.fn(),
};

describe('AutosuggestWrapper', () => {
it('should extract form data from bpk autosuggest', async () => {
const Wrap = () => {
const [inputValue, setInputValue] = useState('');

const inputPropsWithState = {
...inputProps,
value: inputValue,
onChange: (e) => setInputValue(e.target.value)
};
return (
<form data-testid="form">
<AutosuggestWrapper
suggestions={suggestions}
onSuggestionsFetchRequested={onSuggestionsFetchRequested}
onSuggestionsClearRequested={onSuggestionsClearRequested}
getSuggestionValue={getSuggestionValue}
renderSuggestion={renderSuggestion}
inputProps={inputPropsWithState}
/>
</form>
);
}
render(<Wrap />);

const inputField = screen.getByRole('textbox');
await userEvent.type(inputField, 'Edi');
fireEvent.keyDown(inputField, { key: 'Enter' });

await userEvent.click(document.body);

const formData = new FormData(screen.getByTestId('form'));
expect(Object.fromEntries(formData.entries())).toEqual({ value: 'Edinburgh (EDI)' });
});

it('should emit change event when input value is entered', async () => {
const formValidation = jest.fn();
const Wrap = () => {
const [inputValue, setInputValue] = useState('');
useEffect(() => {
document.addEventListener('change', formValidation);
}, []);
const inputPropsWithState = {
...inputProps,
value: inputValue,
onChange: (e) => setInputValue(e.target.value)
};
return (
<form data-testid="form">
<AutosuggestWrapper
data-testid="myInput"
suggestions={suggestions}
onSuggestionsFetchRequested={onSuggestionsFetchRequested}
onSuggestionsClearRequested={onSuggestionsClearRequested}
getSuggestionValue={getSuggestionValue}
renderSuggestion={renderSuggestion}
inputProps={inputPropsWithState}
/>
</form>
);
}
render(<Wrap />);

const inputField = screen.getByRole('textbox');
const form = screen.getByTestId('form');
await userEvent.type(inputField, 'edi');
fireEvent.keyDown(inputField, { key: 'ArrowDown' });
fireEvent.keyDown(inputField, { key: 'Enter' });

await userEvent.click(form);

expect(formValidation).toHaveBeenCalledTimes(1);
});
});
Loading