Skip to content

Commit 62c96ba

Browse files
committed
First pass at URL management
1 parent b8ba566 commit 62c96ba

File tree

5 files changed

+180
-3
lines changed

5 files changed

+180
-3
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
"version": "0.1.0",
44
"private": true,
55
"dependencies": {
6+
"history": "^4.7.2",
67
"prop-types": "^15.6.2",
8+
"query-string": "^6.1.0",
79
"rc-pagination": "^1.16.5",
810
"react": "^16.4.1",
911
"react-dom": "^16.4.1",

src/app-search/AppSearchDriver.js

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as SwiftypeAppSearch from "swiftype-app-search-javascript";
2+
import URLManager from "./URLManager";
23

34
/*
45
* A framework agnostic state manager for App Search apps
@@ -26,13 +27,21 @@ export default class AppSearchDriver {
2627
}) {
2728
this.onStateChange = function() {};
2829
this.searchOptions = searchOptions || {};
29-
this.state = { ...this.state, ...initialState };
30+
31+
this.URLManager = new URLManager();
32+
const urlState = this.URLManager.getStateFromURL();
33+
34+
this.state = { ...this.state, ...initialState, ...urlState };
3035

3136
this.client = SwiftypeAppSearch.createClient({
3237
hostIdentifier: hostIdentifier,
3338
apiKey: searchKey,
3439
engineName: engineName
3540
});
41+
42+
if (this.state.searchTerm) {
43+
this.updateSearchResultsFromCurrentState();
44+
}
3645
}
3746

3847
subscribeToStateChanges(onStateChange) {
@@ -154,4 +163,15 @@ export default class AppSearchDriver {
154163
});
155164
});
156165
};
166+
167+
updateSearchResultsFromCurrentState() {
168+
const { searchTerm, current, filters, resultsPerPage, sort } = this.state;
169+
this.updateSearchResults(
170+
searchTerm,
171+
current,
172+
filters,
173+
resultsPerPage,
174+
sort
175+
);
176+
}
157177
}

src/app-search/URLManager.js

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import createHistory from "history/createBrowserHistory";
2+
import queryString from "query-string";
3+
import { create } from "../types/SortOption";
4+
5+
function isNumeric(num) {
6+
return !isNaN(num);
7+
}
8+
9+
function toSingleValue(val) {
10+
if (Array.isArray(val)) val = val[val.length - 1];
11+
return val;
12+
}
13+
14+
function toSingleValueInteger(num) {
15+
return toInteger(toSingleValue(num));
16+
}
17+
18+
function toInteger(num) {
19+
if (!isNumeric(num)) return;
20+
return parseInt(num, 10);
21+
}
22+
23+
function parseFiltersFromQueryParams(queryParams) {
24+
const filters = Object.keys(queryParams).reduce((acc, paramName) => {
25+
if (paramName.startsWith("f-")) {
26+
let paramValue = queryParams[paramName];
27+
if (!paramValue) return acc;
28+
const filterName = paramName.replace("f-", "");
29+
acc.push({
30+
[filterName]: paramValue
31+
});
32+
}
33+
return acc;
34+
}, []);
35+
36+
if (filters.length === 0) return;
37+
return filters;
38+
}
39+
40+
function parseCurrentFromQueryParams(queryParams) {
41+
return toSingleValueInteger(queryParams.current);
42+
}
43+
44+
function parseSearchTermFromQueryParams(queryParams) {
45+
return toSingleValue(queryParams.q);
46+
}
47+
48+
function parseSortFromQueryParams(queryParams) {
49+
const sortBy = toSingleValue(queryParams["sort-by"]);
50+
const sortDirection = toSingleValue(queryParams["sort-direction"]);
51+
52+
if (sortBy) {
53+
return create({
54+
value: sortBy,
55+
direction: sortDirection
56+
});
57+
}
58+
59+
return;
60+
}
61+
62+
function parseSizeFromQueryParams(queryParams) {
63+
return toSingleValueInteger(queryParams.size);
64+
}
65+
66+
/**
67+
* The URL Manager is responsible for synchronizing state between
68+
* AppSearchDriver and the URL. There are 3 main cases to handle when
69+
* synchronizing:
70+
*
71+
* 1. When the app loads, AppSearchDriver will need to
72+
* read the current state from the URL, in order to perform the search
73+
* expressed by the query string. `getStateFromURL` is used for this case.
74+
*
75+
* 2. When the URL changes as a result of `pushState` or `replaceState`,
76+
* AppSearchDriver will need to be notified and given the updated state, so that
77+
* it can re-run the current search. `onURLStateChange` is used for this case.
78+
*
79+
* 3. When state changes in AppSearchDriver, the URL will need to be updated
80+
* to reflect those changes. `onURLStateChange` is used for this case.
81+
*/
82+
83+
export default class URLManager {
84+
constructor() {
85+
this.history = createHistory();
86+
}
87+
88+
getStateFromURL() {
89+
const state = paramsToState(
90+
queryString.parse(this.history.location.search)
91+
);
92+
return state;
93+
}
94+
95+
pushStateToURL(state) {}
96+
97+
onURLStateChange() {}
98+
}
99+
100+
function paramsToState(queryParams) {
101+
const state = {
102+
current: parseCurrentFromQueryParams(queryParams),
103+
filters: parseFiltersFromQueryParams(queryParams),
104+
searchTerm: parseSearchTermFromQueryParams(queryParams),
105+
resultsPerPage: parseSizeFromQueryParams(queryParams),
106+
sort: parseSortFromQueryParams(queryParams)
107+
};
108+
109+
return Object.keys(state).reduce((acc, key) => {
110+
const value = state[key];
111+
if (value) acc[key] = value;
112+
return acc;
113+
}, {});
114+
}

src/types/SortOption.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import PropTypes from "prop-types";
22

3+
const DIRECTIONS = ["asc", "desc", ""];
4+
35
export default PropTypes.shape({
46
// A display name, like "Name"
57
name: PropTypes.string,
@@ -10,6 +12,10 @@ export default PropTypes.shape({
1012
});
1113

1214
export function create({ name, value, direction }) {
15+
if (!DIRECTIONS.includes(direction)) {
16+
direction = "";
17+
}
18+
1319
return {
1420
name,
1521
value,

yarn.lock

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3187,6 +3187,16 @@ he@1.1.x:
31873187
version "1.1.1"
31883188
resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd"
31893189

3190+
history@^4.7.2:
3191+
version "4.7.2"
3192+
resolved "https://registry.yarnpkg.com/history/-/history-4.7.2.tgz#22b5c7f31633c5b8021c7f4a8a954ac139ee8d5b"
3193+
dependencies:
3194+
invariant "^2.2.1"
3195+
loose-envify "^1.2.0"
3196+
resolve-pathname "^2.2.0"
3197+
value-equal "^0.4.0"
3198+
warning "^3.0.0"
3199+
31903200
hmac-drbg@^1.0.0:
31913201
version "1.0.1"
31923202
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
@@ -3437,7 +3447,7 @@ interpret@^1.0.0:
34373447
version "1.1.0"
34383448
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614"
34393449

3440-
invariant@^2.2.2:
3450+
invariant@^2.2.1, invariant@^2.2.2:
34413451
version "2.2.4"
34423452
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
34433453
dependencies:
@@ -4362,7 +4372,7 @@ longest@^1.0.1:
43624372
version "1.0.1"
43634373
resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
43644374

4365-
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1:
4375+
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1:
43664376
version "1.4.0"
43674377
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
43684378
dependencies:
@@ -5676,6 +5686,13 @@ query-string@^4.1.0:
56765686
object-assign "^4.1.0"
56775687
strict-uri-encode "^1.0.0"
56785688

5689+
query-string@^6.1.0:
5690+
version "6.1.0"
5691+
resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.1.0.tgz#01e7d69f6a0940dac67a937d6c6325647aa4532a"
5692+
dependencies:
5693+
decode-uri-component "^0.2.0"
5694+
strict-uri-encode "^2.0.0"
5695+
56795696
querystring-es3@^0.2.0:
56805697
version "0.2.1"
56815698
resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"
@@ -6098,6 +6115,10 @@ resolve-from@^3.0.0:
60986115
version "3.0.0"
60996116
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748"
61006117

6118+
resolve-pathname@^2.2.0:
6119+
version "2.2.0"
6120+
resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-2.2.0.tgz#7e9ae21ed815fd63ab189adeee64dc831eefa879"
6121+
61016122
resolve-url@^0.2.1:
61026123
version "0.2.1"
61036124
resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
@@ -6582,6 +6603,10 @@ strict-uri-encode@^1.0.0:
65826603
version "1.1.0"
65836604
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
65846605

6606+
strict-uri-encode@^2.0.0:
6607+
version "2.0.0"
6608+
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546"
6609+
65856610
string-length@^1.0.1:
65866611
version "1.0.1"
65876612
resolved "https://registry.yarnpkg.com/string-length/-/string-length-1.0.1.tgz#56970fb1c38558e9e70b728bf3de269ac45adfac"
@@ -7109,6 +7134,10 @@ validate-npm-package-license@^3.0.1:
71097134
spdx-correct "^3.0.0"
71107135
spdx-expression-parse "^3.0.0"
71117136

7137+
value-equal@^0.4.0:
7138+
version "0.4.0"
7139+
resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-0.4.0.tgz#c5bdd2f54ee093c04839d71ce2e4758a6890abc7"
7140+
71127141
vary@~1.1.2:
71137142
version "1.1.2"
71147143
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
@@ -7137,6 +7166,12 @@ walker@~1.0.5:
71377166
dependencies:
71387167
makeerror "1.0.x"
71397168

7169+
warning@^3.0.0:
7170+
version "3.0.0"
7171+
resolved "https://registry.yarnpkg.com/warning/-/warning-3.0.0.tgz#32e5377cb572de4ab04753bdf8821c01ed605b7c"
7172+
dependencies:
7173+
loose-envify "^1.0.0"
7174+
71407175
watch@~0.10.0:
71417176
version "0.10.0"
71427177
resolved "https://registry.yarnpkg.com/watch/-/watch-0.10.0.tgz#77798b2da0f9910d595f1ace5b0c2258521f21dc"

0 commit comments

Comments
 (0)