diff --git a/src/app/WardrobePage/SearchFooter.js b/src/app/WardrobePage/SearchFooter.js
new file mode 100644
index 0000000..2c6e2df
--- /dev/null
+++ b/src/app/WardrobePage/SearchFooter.js
@@ -0,0 +1,46 @@
+import React from "react";
+import * as Sentry from "@sentry/react";
+import { Box, Flex } from "@chakra-ui/react";
+import SearchToolbar, { emptySearchQuery } from "./SearchToolbar";
+import { MajorErrorMessage, TestErrorSender, useLocalStorage } from "../util";
+
+/**
+ * SearchFooter appears on large screens only, to let you search for new items
+ * while still keeping the rest of the item screen open!
+ */
+function SearchFooter() {
+ const [canUseSearchFooter, setCanUseSearchFooter] = useLocalStorage(
+ "DTIFeatureFlagCanUseSearchFooter",
+ false
+ );
+
+ React.useEffect(() => {
+ if (window.location.search.includes("feature-flag-can-use-search-footer")) {
+ setCanUseSearchFooter(true);
+ }
+ }, [setCanUseSearchFooter]);
+
+ const [query, setQuery] = React.useState(emptySearchQuery);
+
+ // TODO: Show the new footer to other users, too!
+ if (!canUseSearchFooter) {
+ return null;
+ }
+
+ return (
+
+
+
+
+
+ Add new items:
+
+
+
+
+
+
+ );
+}
+
+export default SearchFooter;
diff --git a/src/app/WardrobePage/SearchToolbar.js b/src/app/WardrobePage/SearchToolbar.js
index 46e53ab..f30cd2b 100644
--- a/src/app/WardrobePage/SearchToolbar.js
+++ b/src/app/WardrobePage/SearchToolbar.js
@@ -53,6 +53,7 @@ function SearchToolbar({
showItemsLabel = false,
background = null,
boxShadow = null,
+ ...props
}) {
const [suggestions, setSuggestions] = React.useState([]);
const [advancedSearchIsOpen, setAdvancedSearchIsOpen] = React.useState(false);
@@ -182,139 +183,144 @@ function SearchToolbar({
const focusBorderColor = useColorModeValue("green.600", "green.400");
return (
- {
- // HACK: I'm not sure why, but apparently this gets called with value
- // set to the _chosen suggestion_ after choosing it? Has that
- // always happened? Idk? Let's just, gate around it, I guess?
- if (typeof value === "string") {
- setSuggestions(getSuggestions(value, query, zoneLabels, isLoggedIn));
- }
- }}
- onSuggestionSelected={(e, { suggestion }) => {
- onChange({
- ...query,
- // If the suggestion was from typing, remove the last word of the
- // query value. Or, if it was from Advanced Search, leave it alone!
- value: advancedSearchIsOpen
- ? query.value
- : removeLastWord(query.value),
- filterToZoneLabel: suggestion.zoneLabel || query.filterToZoneLabel,
- filterToItemKind: suggestion.itemKind || query.filterToItemKind,
- filterToCurrentUserOwnsOrWants:
- suggestion.userOwnsOrWants || query.filterToCurrentUserOwnsOrWants,
- });
- }}
- getSuggestionValue={(zl) => zl}
- alwaysRenderSuggestions={true}
- renderSuggestion={renderSuggestion}
- renderSuggestionsContainer={renderSuggestionsContainer}
- renderInputComponent={(inputProps) => (
-
- {queryFilterText ? (
-
-
- {queryFilterText}
-
- ) : (
-
-
-
- )}
-
-
- {!searchQueryIsEmpty(query) && (
-
+
+ {
+ // HACK: I'm not sure why, but apparently this gets called with value
+ // set to the _chosen suggestion_ after choosing it? Has that
+ // always happened? Idk? Let's just, gate around it, I guess?
+ if (typeof value === "string") {
+ setSuggestions(
+ getSuggestions(value, query, zoneLabels, isLoggedIn)
+ );
+ }
+ }}
+ onSuggestionSelected={(e, { suggestion }) => {
+ onChange({
+ ...query,
+ // If the suggestion was from typing, remove the last word of the
+ // query value. Or, if it was from Advanced Search, leave it alone!
+ value: advancedSearchIsOpen
+ ? query.value
+ : removeLastWord(query.value),
+ filterToZoneLabel: suggestion.zoneLabel || query.filterToZoneLabel,
+ filterToItemKind: suggestion.itemKind || query.filterToItemKind,
+ filterToCurrentUserOwnsOrWants:
+ suggestion.userOwnsOrWants ||
+ query.filterToCurrentUserOwnsOrWants,
+ });
+ }}
+ getSuggestionValue={(zl) => zl}
+ alwaysRenderSuggestions={true}
+ renderSuggestion={renderSuggestion}
+ renderSuggestionsContainer={renderSuggestionsContainer}
+ renderInputComponent={(inputProps) => (
+
+ {queryFilterText ? (
+
+
+ {queryFilterText}
+
+ ) : (
+
+
+
+ )}
+
+
+ {!searchQueryIsEmpty(query) && (
+
+ }
+ color="gray.400"
+ variant="ghost"
+ height="100%"
+ marginLeft="1"
+ aria-label="Clear search"
+ onClick={() => {
+ setSuggestions([]);
+ onChange(emptySearchQuery);
+ }}
+ />
+
+ )}
+
}
+ icon={
+ advancedSearchIsOpen ? (
+
+ ) : (
+
+ )
+ }
color="gray.400"
variant="ghost"
height="100%"
- marginLeft="1"
- aria-label="Clear search"
- onClick={() => {
- setSuggestions([]);
- onChange(emptySearchQuery);
- }}
+ aria-label="Open advanced search"
+ onClick={() => setAdvancedSearchIsOpen((isOpen) => !isOpen)}
/>
- )}
-
-
- ) : (
-
- )
- }
- color="gray.400"
- variant="ghost"
- height="100%"
- aria-label="Open advanced search"
- onClick={() => setAdvancedSearchIsOpen((isOpen) => !isOpen)}
- />
-
-
-
- )}
- inputProps={{
- placeholder: "Search all items…",
- focusBorderColor: focusBorderColor,
- value: query.value || "",
- ref: searchQueryRef,
- minWidth: 0,
- "data-test-id": "item-search-input",
- onChange: (e, { newValue, method }) => {
- // The Autosuggest tries to change the _entire_ value of the element
- // when navigating suggestions, which isn't actually what we want.
- // Only accept value changes that are typed by the user!
- if (method === "type") {
- onChange({ ...query, value: newValue });
- }
- },
- onKeyDown: (e) => {
- if (e.key === "Escape") {
- if (suggestions.length > 0) {
- setSuggestions([]);
- return;
+
+
+ )}
+ inputProps={{
+ placeholder: "Search all items…",
+ focusBorderColor: focusBorderColor,
+ value: query.value || "",
+ ref: searchQueryRef,
+ minWidth: 0,
+ "data-test-id": "item-search-input",
+ onChange: (e, { newValue, method }) => {
+ // The Autosuggest tries to change the _entire_ value of the element
+ // when navigating suggestions, which isn't actually what we want.
+ // Only accept value changes that are typed by the user!
+ if (method === "type") {
+ onChange({ ...query, value: newValue });
}
- onChange(emptySearchQuery);
- e.target.blur();
- } else if (e.key === "Enter") {
- // Pressing Enter doesn't actually submit because it's all on
- // debounce, but it can be a declaration that the query is done, so
- // filter suggestions should go away!
- if (suggestions.length > 0) {
- setSuggestions([]);
- return;
+ },
+ onKeyDown: (e) => {
+ if (e.key === "Escape") {
+ if (suggestions.length > 0) {
+ setSuggestions([]);
+ return;
+ }
+ onChange(emptySearchQuery);
+ e.target.blur();
+ } else if (e.key === "Enter") {
+ // Pressing Enter doesn't actually submit because it's all on
+ // debounce, but it can be a declaration that the query is done, so
+ // filter suggestions should go away!
+ if (suggestions.length > 0) {
+ setSuggestions([]);
+ return;
+ }
+ } else if (e.key === "ArrowDown") {
+ if (suggestions.length > 0) {
+ return;
+ }
+ onMoveFocusDownToResults(e);
+ } else if (e.key === "Backspace" && e.target.selectionStart === 0) {
+ onChange({
+ ...query,
+ filterToItemKind: null,
+ filterToZoneLabel: null,
+ filterToCurrentUserOwnsOrWants: null,
+ });
}
- } else if (e.key === "ArrowDown") {
- if (suggestions.length > 0) {
- return;
- }
- onMoveFocusDownToResults(e);
- } else if (e.key === "Backspace" && e.target.selectionStart === 0) {
- onChange({
- ...query,
- filterToItemKind: null,
- filterToZoneLabel: null,
- filterToCurrentUserOwnsOrWants: null,
- });
- }
- },
- }}
- />
+ },
+ }}
+ />
+
);
}
diff --git a/src/app/WardrobePage/WardrobePageLayout.js b/src/app/WardrobePage/WardrobePageLayout.js
index f5dc687..e205f4d 100644
--- a/src/app/WardrobePage/WardrobePageLayout.js
+++ b/src/app/WardrobePage/WardrobePageLayout.js
@@ -1,8 +1,15 @@
import React from "react";
-import { Box, Grid, useColorModeValue } from "@chakra-ui/react";
+import { Box, Grid, useColorModeValue, useToken } from "@chakra-ui/react";
+import { useCommonStyles } from "../util";
-function WardrobePageLayout({ previewAndControls, itemsAndSearch }) {
+function WardrobePageLayout({
+ previewAndControls = null,
+ itemsAndMaybeSearchPanel = null,
+ searchFooter = null,
+}) {
const itemsAndSearchBackground = useColorModeValue("white", "gray.900");
+ const searchBackground = useCommonStyles().bodyBackground;
+ const searchShadowColorValue = useToken("colors", "gray.400");
return (
{previewAndControls}
-
- {itemsAndSearch}
+
+ {itemsAndMaybeSearchPanel}
+
+
+ {searchFooter}
diff --git a/src/app/WardrobePage/index.js b/src/app/WardrobePage/index.js
index 561e06f..572fa81 100644
--- a/src/app/WardrobePage/index.js
+++ b/src/app/WardrobePage/index.js
@@ -4,6 +4,7 @@ import { useToast } from "@chakra-ui/react";
import { loadable } from "../util";
import ItemsAndSearchPanels from "./ItemsAndSearchPanels";
+import SearchFooter from "./SearchFooter";
import SupportOnly from "./support/SupportOnly";
import useOutfitSaving from "./useOutfitSaving";
import useOutfitState, { OutfitStateContext } from "./useOutfitState";
@@ -104,7 +105,7 @@ function WardrobePage() {
dispatchToOutfit={dispatchToOutfit}
/>
}
- itemsAndSearch={
+ itemsAndMaybeSearchPanel={
}
+ searchFooter={}
/>
);