Skip to content

Commit

Permalink
feat: add remaining examples to InputSearch docs and additional testi…
Browse files Browse the repository at this point in the history
…ng examples and validation
  • Loading branch information
wp-aberg committed Jul 14, 2023
1 parent d4f1528 commit e527dd9
Show file tree
Hide file tree
Showing 22 changed files with 646 additions and 148 deletions.
361 changes: 281 additions & 80 deletions build.washingtonpost.com/docs/components/input-search.mdx

Large diffs are not rendered by default.

99 changes: 67 additions & 32 deletions build.washingtonpost.com/pages/resources/working-examples.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as React from "react";
import { useState } from "react";
import { useForm, Controller } from "react-hook-form";
import { isPossiblePhoneNumber } from "react-phone-number-input";
import { matchSorter } from "match-sorter";
import {
Box,
Button,
Expand All @@ -17,6 +18,8 @@ import {
styled,
} from "@washingtonpost/wpds-ui-kit";

import { cities } from "@washingtonpost/wpds-input-search/src/cities";

const STATES = [
"Alabama",
"Alaska",
Expand Down Expand Up @@ -128,6 +131,7 @@ const Form = () => {
handleSubmit,
register,
reset,
clearErrors,
watch,
} = useForm<FormInputType>({});

Expand All @@ -137,6 +141,22 @@ const Form = () => {

const [checked, setChecked] = useState(false);

const useCityMatch = (term: string) => {
return React.useMemo(
() =>
term.trim() === ""
? null
: matchSorter(cities, term, {
keys: [(item) => `${item.city}, ${item.state}`],
}),
[term]
);
};

const [term, setTerm] = React.useState("");

const results: { city: string; state: string }[] | null = useCityMatch(term);

return (
<>
<Headline>Form Example</Headline>
Expand All @@ -147,21 +167,6 @@ const Form = () => {
</p>
<FormContainer>
<form onSubmit={handleSubmit(onSubmit)} noValidate>
<Row>
<InputWrapper>
<InputSearch.Root aria-label="search cities">
<InputSearch.Input name="city" id="city" />
<InputSearch.Popover>
<InputSearch.List>
<InputSearch.ListItem value="Boston" />
<InputSearch.ListItem value="New York" />
<InputSearch.ListItem value="Philadelphia" />
<InputSearch.ListItem value="Washington D.C." />
</InputSearch.List>
</InputSearch.Popover>
</InputSearch.Root>
</InputWrapper>
</Row>
<Row>
<InputWrapper>
<InputText
Expand Down Expand Up @@ -272,23 +277,53 @@ const Form = () => {
</Row>
<Row>
<InputWrapper>
<InputText
label="City"
id="city"
error={!!errors.city}
errorMessage={errors.city?.message}
required
{...register("city", {
required: {
value: true,
message: "City is required",
},
minLength: {
value: 5,
message: "Please add a valid city",
},
})}
/>
<InputSearch.Root
aria-label="City"
openOnFocus
onSelect={(value) => {
console.log("select?", value);
if (value) {
clearErrors("city");
}
}}
>
<InputSearch.Input
id="city"
label="City"
error={!!errors.city}
errorMessage={errors.city?.message}
required
{...register("city", {
required: {
value: true,
message: "City is required",
},
minLength: {
value: 5,
message: "Please add a valid city",
},
onChange: (event) => {
setTerm(event.target.value);
},
})}
/>
{results && (
<InputSearch.Popover>
{results.length > 0 ? (
<InputSearch.List>
{results.slice(0, 20).map((result) => (
<InputSearch.ListItem
key={`${result.city.toLowerCase()}, ${result.state.toLowerCase()}`}
value={`${result.city}, ${result.state}`}
/>
))}
</InputSearch.List>
) : (
<InputSearch.EmptyState />
)}
</InputSearch.Popover>
)}
</InputSearch.Root>
</InputWrapper>
</Row>
<Row>
Expand Down
18 changes: 10 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions ui/input-search/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
},
"dependencies": {
"@reach/combobox": "^0.18.0",
"@reach/popover": "^0.18.0",
"@washingtonpost/wpds-assets": "^1.18.0",
"@washingtonpost/wpds-icon": "1.8.5",
"@washingtonpost/wpds-input-label": "1.8.5",
Expand Down
10 changes: 10 additions & 0 deletions ui/input-search/src/InputSearchEmptyState.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import * as React from "react";
import { render, screen } from "@testing-library/react";
import { InputSearchEmptyState } from "./InputSearchEmptyState";

describe("InputSearchEmptyState", () => {
test("renders visibly into the document", () => {
render(<InputSearchEmptyState />);
expect(screen.getByText("No results found")).toBeInTheDocument();
});
});
28 changes: 28 additions & 0 deletions ui/input-search/src/InputSearchInput.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import * as React from "react";
import { render, screen } from "@testing-library/react";
import { InputSearchInput } from "./InputSearchInput";
import { InputSearchRoot } from "./InputSearchRoot";
import { InputSearchPopover } from "./InputSearchPopover";

describe("InputSearchInput", () => {
const customRender = (ui, contextProps) => {
return render(
<InputSearchRoot>
{ui}
<InputSearchPopover {...contextProps} />
</InputSearchRoot>
);
};
test("renders visibly into the document", () => {
customRender(<InputSearchInput name="test" id="test" label="Test" />, {});
expect(screen.getByLabelText("Test")).toBeInTheDocument();
});
test("uses contexts portal prop", () => {
customRender(<InputSearchInput name="test" id="test" label="Test" />, {
portal: false,
});
expect(screen.getByTestId("border-style-override")).toHaveStyle(
"--wpds-colors-signal: var(--wpds-colors-subtle)"
);
});
});
2 changes: 2 additions & 0 deletions ui/input-search/src/InputSearchInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export const InputSearchInput = React.forwardRef<
InputSearchInputProps
>(({ label = "Search", name, id, ...rest }: InputSearchInputProps, ref) => {
const { disabled, usePortal } = React.useContext(InputSearchContext);
console.log("usePortal", usePortal);
return (
<div
style={
Expand All @@ -36,6 +37,7 @@ export const InputSearchInput = React.forwardRef<
"--wpds-colors-signal": "var(--wpds-colors-subtle)",
} as React.CSSProperties)
}
data-testid="border-style-override"
>
<ComboboxInput
as={InputText}
Expand Down
24 changes: 24 additions & 0 deletions ui/input-search/src/InputSearchItemText.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import * as React from "react";
import { render, screen } from "@testing-library/react";
import { InputSearchItemText } from "./InputSearchItemText";
import { InputSearchRoot } from "./InputSearchRoot";
import { InputSearchListItem } from "./InputSearchListItem";
import { InputSearchList } from "./InputSearchList";

describe("InputSearchItemText", () => {
const customRender = (ui, contextProps) => {
return render(
<InputSearchRoot>
<InputSearchList>
<InputSearchListItem {...contextProps}>{ui}</InputSearchListItem>
</InputSearchList>
</InputSearchRoot>
);
};
test("renders visibly into the document", () => {
customRender(<InputSearchItemText>&#x2766;</InputSearchItemText>, {
value: "test",
});
expect(screen.getByText("test")).toBeInTheDocument();
});
});
15 changes: 15 additions & 0 deletions ui/input-search/src/InputSearchList.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import * as React from "react";
import { render, screen } from "@testing-library/react";
import { InputSearchList } from "./InputSearchList";
import { InputSearchRoot } from "./InputSearchRoot";

describe("InputSearchList", () => {
const customRender = (ui, contextProps) => {
return render(<InputSearchRoot {...contextProps}>{ui}</InputSearchRoot>);
};

test("renders visibly into the document", () => {
customRender(<InputSearchList />, {});
expect(screen.getByRole("listbox")).toBeInTheDocument();
});
});
13 changes: 6 additions & 7 deletions ui/input-search/src/InputSearchList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { styled } from "@washingtonpost/wpds-theme";

const StyledList = styled(ComboboxList, {
marginBlock: 0,
maxHeight: "300px",
overflowY: "auto",
paddingInlineStart: 0,
position: "relative",
listStyleType: "none",
Expand Down Expand Up @@ -32,19 +34,16 @@ export const InputSearchList = ({
) as HTMLElement;
if (!selectedEl) return;

const parentEl = listEl.parentElement;
if (!parentEl) return;

const listTop = parentEl.scrollTop;
const listBottom = listTop + parentEl.clientHeight;
const listTop = listEl.scrollTop;
const listBottom = listTop + listEl.clientHeight;

const selectedTop = selectedEl.offsetTop;
const selectedBottom = selectedTop + selectedEl.clientHeight;

if (selectedTop < listTop) {
parentEl.scrollTop -= listTop - selectedTop;
listEl.scrollTop -= listTop - selectedTop;
} else if (selectedBottom > listBottom) {
parentEl.scrollTop += selectedBottom - listBottom;
listEl.scrollTop += selectedBottom - listBottom;
}
}
}, [navigationValue, state]);
Expand Down
10 changes: 10 additions & 0 deletions ui/input-search/src/InputSearchListHeading.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import * as React from "react";
import { render, screen } from "@testing-library/react";
import { InputSearchListHeading } from "./InputSearchListHeading";

describe("InputSearchListHeading", () => {
test("renders visibly into the document", () => {
render(<InputSearchListHeading>test</InputSearchListHeading>);
expect(screen.getByText("test")).toBeInTheDocument();
});
});
19 changes: 19 additions & 0 deletions ui/input-search/src/InputSearchListItem.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import * as React from "react";
import { render, screen } from "@testing-library/react";
import { InputSearchListItem } from "./InputSearchListItem";
import { InputSearchRoot } from "./InputSearchRoot";
import { InputSearchList } from "./InputSearchList";

describe("InputSearchItemText", () => {
const customRender = (ui, contextProps) => {
return render(
<InputSearchRoot>
<InputSearchList {...contextProps}>{ui}</InputSearchList>
</InputSearchRoot>
);
};
test("renders visibly into the document", () => {
customRender(<InputSearchListItem value="test" />, {});
expect(screen.getByRole("option")).toBeInTheDocument();
});
});
6 changes: 4 additions & 2 deletions ui/input-search/src/InputSearchListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { styled, theme } from "@washingtonpost/wpds-theme";

const StyledListItem = styled(ComboboxOption, {
color: theme.colors.primary,
fontFamily: theme.fonts.meta,
fontSize: theme.fontSizes["100"],
fontWeight: theme.fontWeights.light,
paddingBlock: "$050",
Expand All @@ -19,8 +20,9 @@ const StyledListItem = styled(ComboboxOption, {
},
});

export type InputSearchListItemProps = React.ComponentPropsWithRef<
typeof StyledListItem
export type InputSearchListItemProps = Omit<
React.ComponentPropsWithRef<typeof StyledListItem>,
"index"
>;

export const InputSearchListItem = ({
Expand Down
10 changes: 10 additions & 0 deletions ui/input-search/src/InputSearchLoadingState.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import * as React from "react";
import { render, screen } from "@testing-library/react";
import { InputSearchLoadingState } from "./InputSearchLoadingState";

describe("InputSearchLoadingState", () => {
test("renders visibly into the document", () => {
render(<InputSearchLoadingState />);
expect(screen.getByText("Loading")).toBeInTheDocument();
});
});
20 changes: 20 additions & 0 deletions ui/input-search/src/InputSearchOtherState.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import * as React from "react";
import { render, screen } from "@testing-library/react";
import { InputSearchOtherState } from "./InputSearchOtherState";
import { Icon } from "@washingtonpost/wpds-icon";
import { Settings } from "@washingtonpost/wpds-assets";

describe("InputSearchOtherState", () => {
test("renders visibly into the document", () => {
render(
<InputSearchOtherState
icon={
<Icon label="test">
<Settings />
</Icon>
}
/>
);
expect(screen.getByText("test")).toBeInTheDocument();
});
});
Loading

4 comments on commit e527dd9

@vercel
Copy link

@vercel vercel bot commented on e527dd9 Jul 14, 2023

Choose a reason for hiding this comment

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

@vercel
Copy link

@vercel vercel bot commented on e527dd9 Jul 14, 2023

Choose a reason for hiding this comment

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

Successfully deployed to the following URLs:

wpds-ui-kit-vitejs-example – ./apps/vite-project

wpds-ui-kit-vitejs-example-git-main.preview.now.washingtonpost.com
wpds-ui-kit-vitejs-example.preview.now.washingtonpost.com

@vercel
Copy link

@vercel vercel bot commented on e527dd9 Jul 14, 2023

Choose a reason for hiding this comment

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

Successfully deployed to the following URLs:

wpds-ui-kit – ./build.washingtonpost.com

wpds-ui-kit.preview.now.washingtonpost.com
wpds-ui-kit-git-main.preview.now.washingtonpost.com
build.washingtonpost.com

@vercel
Copy link

@vercel vercel bot commented on e527dd9 Jul 14, 2023

Choose a reason for hiding this comment

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

Successfully deployed to the following URLs:

wpds-ui-kit-storybook – ./

wpds-ui-kit-storybook-git-main.preview.now.washingtonpost.com
wpds-ui-kit-storybook.preview.now.washingtonpost.com

Please sign in to comment.