Skip to content

Commit 1c39e13

Browse files
committed
2927: Changed options widget
1 parent 16eac74 commit 1c39e13

File tree

3 files changed

+125
-138
lines changed

3 files changed

+125
-138
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file.
77

88
- [#279](https://github.com/os2display/display-admin-client/pull/279)
99
- Eventdatabase v2 feed source - Change subscription endpoint.
10+
- Eventdatabase v2 feed source - Fixed options load.
1011
- [#271](https://github.com/os2display/display-admin-client/pull/271)
1112
- Added new feed source: Eventdatabasen v2.
1213

src/components/slide/content/poster/poster-single-search.jsx

Lines changed: 66 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import { React, useEffect, useRef, useState } from "react";
1+
import { React, useEffect, useState } from "react";
22
import { Button, Row } from "react-bootstrap";
33
import Col from "react-bootstrap/Col";
4-
import AsyncSelect from "react-select/async";
54
import { useTranslation } from "react-i18next";
65
import PropTypes from "prop-types";
6+
import { MultiSelect } from "react-multi-select-component";
7+
import Form from "react-bootstrap/Form";
78
import FormInput from "../../../util/forms/form-input";
89
import Select from "../../../util/forms/select";
910
import { getHeaders, loadDropdownOptionsPromise } from "./poster-helper";
@@ -28,27 +29,29 @@ function PosterSingleSearch({
2829
const [singleSearchType, setSingleSearchType] = useState("title");
2930
const [singleSearchTypeValue, setSingleSearchTypeValue] = useState("");
3031

31-
const timeoutRef = useRef(null);
32-
33-
const debounceOptions = (inputValue) => {
34-
// Debounce result to avoid searching while typing.
35-
return new Promise((resolve, reject) => {
36-
if (timeoutRef.current) {
37-
clearTimeout(timeoutRef.current);
38-
}
39-
40-
timeoutRef.current = setTimeout(() => {
41-
loadDropdownOptionsPromise(
42-
optionsEndpoint,
43-
getHeaders(),
44-
inputValue,
45-
singleSearchType
46-
)
47-
.then((data) => resolve(data))
48-
.catch((reason) => reject(reason));
49-
}, 500);
50-
});
51-
};
32+
const [locations, setLocations] = useState([]);
33+
const [tags, setTags] = useState([]);
34+
const [organizations, setOrganizations] = useState([]);
35+
36+
useEffect(() => {
37+
loadDropdownOptionsPromise(optionsEndpoint, getHeaders(), "", "tags").then(
38+
(r) => setTags(r)
39+
);
40+
41+
loadDropdownOptionsPromise(
42+
optionsEndpoint,
43+
getHeaders(),
44+
"",
45+
"locations"
46+
).then((r) => setLocations(r));
47+
48+
loadDropdownOptionsPromise(
49+
optionsEndpoint,
50+
getHeaders(),
51+
"",
52+
"organizations"
53+
).then((r) => setOrganizations(r));
54+
}, []);
5255

5356
const singleSearchFetch = () => {
5457
const params = {
@@ -112,6 +115,18 @@ function PosterSingleSearch({
112115
},
113116
];
114117

118+
/**
119+
* A callback on changed data.
120+
*
121+
* @param {Array} data The data to call back with
122+
*/
123+
const changeData = (data) => {
124+
if (data?.length > 0) {
125+
const index = data?.length > 1 ? 1 : 0;
126+
setSingleSearchTypeValue(data[index]);
127+
}
128+
};
129+
115130
useEffect(() => {
116131
setSingleSearchTypeValue("");
117132
}, [singleSearchType]);
@@ -140,64 +155,53 @@ function PosterSingleSearch({
140155
)}
141156
{singleSearchType === "locations" && (
142157
<Col>
143-
<label
144-
className="form-label"
145-
htmlFor="single-search-select-locations"
146-
>
158+
<Form.Label htmlFor="single-search-select-locations">
147159
{t("single-search-select")}
148-
</label>
149-
<AsyncSelect
160+
</Form.Label>
161+
<MultiSelect
150162
id="single-search-select-locations"
151-
isClearable
152-
isSearchable
153-
defaultOptions
163+
label={t("single-search-select")}
164+
name="locations"
165+
onChange={changeData}
166+
options={locations}
167+
value={singleSearchTypeValue ? [singleSearchTypeValue] : []}
154168
placeholder={t("single-search-placeholder")}
155-
loadOptions={debounceOptions}
156-
value={singleSearchTypeValue}
157-
onChange={(newValue) => {
158-
setSingleSearchTypeValue(newValue);
159-
}}
169+
labelledBy="single-search-select-locations"
160170
/>
161171
</Col>
162172
)}
163173
{singleSearchType === "organizations" && (
164174
<Col>
165-
<label
166-
className="form-label"
167-
htmlFor="single-search-select-organizations"
168-
>
175+
<Form.Label htmlFor="single-search-select-organizations">
169176
{t("single-search-select")}
170-
</label>
171-
<AsyncSelect
177+
</Form.Label>
178+
<MultiSelect
172179
id="single-search-select-organizations"
173-
isClearable
174-
isSearchable
175-
defaultOptions
180+
label={t("single-search-select")}
181+
name="organizations"
182+
singleSelect
183+
options={organizations}
184+
onChange={changeData}
185+
value={singleSearchTypeValue ? [singleSearchTypeValue] : []}
176186
placeholder={t("single-search-placeholder")}
177-
loadOptions={debounceOptions}
178-
value={singleSearchTypeValue}
179-
onChange={(newValue) => {
180-
setSingleSearchTypeValue(newValue);
181-
}}
187+
labelledBy="single-search-select-organizations"
182188
/>
183189
</Col>
184190
)}
185191
{singleSearchType === "tags" && (
186192
<Col>
187-
<label className="form-label" htmlFor="single-search-select-tags">
193+
<Form.Label htmlFor="single-search-select-tags">
188194
{t("single-search-select")}
189-
</label>
190-
<AsyncSelect
195+
</Form.Label>
196+
<MultiSelect
191197
id="single-search-select-tags"
192-
isClearable
193-
isSearchable
194-
defaultOptions
198+
label={t("single-search-select")}
199+
name="tags"
200+
options={tags}
201+
onChange={changeData}
202+
value={singleSearchTypeValue ? [singleSearchTypeValue] : []}
195203
placeholder={t("single-search-placeholder")}
196-
loadOptions={debounceOptions}
197-
value={singleSearchTypeValue}
198-
onChange={(newValue) => {
199-
setSingleSearchTypeValue(newValue);
200-
}}
204+
labelledBy="single-search-select-tags"
201205
/>
202206
</Col>
203207
)}

src/components/slide/content/poster/poster-subscription-criteria.jsx

Lines changed: 58 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import AsyncSelect from "react-select/async";
2-
import { React, useRef } from "react";
2+
import {React, useEffect, useRef, useState} from "react";
33
import { useTranslation } from "react-i18next";
44
import PropTypes from "prop-types";
55
import { getHeaders, loadDropdownOptionsPromise } from "./poster-helper";
6+
import {MultiSelect} from "react-multi-select-component";
67

78
/**
89
* @param {object} props The props.
@@ -27,47 +28,35 @@ function PosterSubscriptionCriteria({
2728
}) {
2829
const { t } = useTranslation("common", { keyPrefix: "poster-selector-v2" });
2930

30-
const locationTimeoutRef = useRef(null);
31-
const organizationTimeoutRef = useRef(null);
32-
const tagTimeoutRef = useRef(null);
33-
3431
// The user can choose between 1-10 entries to display.
3532
const numberOptions = Array.from(Array(10).keys());
3633

37-
const debounceOptions = (inputValue, type) => {
38-
// Debounce promise.
39-
return new Promise((resolve, reject) => {
40-
let timeoutRef = null;
34+
const [locations, setLocations] = useState([]);
35+
const [tags, setTags] = useState([]);
36+
const [organizations, setOrganizations] = useState([]);
37+
38+
useEffect(() => {
39+
loadDropdownOptionsPromise(optionsEndpoint, getHeaders(), "", "tags").then(
40+
(r) => setTags(r)
41+
);
4142

42-
switch (type) {
43-
case "locations":
44-
timeoutRef = locationTimeoutRef;
45-
break;
46-
case "organizations":
47-
timeoutRef = organizationTimeoutRef;
48-
break;
49-
case "tags":
50-
timeoutRef = tagTimeoutRef;
51-
break;
52-
default:
53-
return;
54-
}
43+
loadDropdownOptionsPromise(
44+
optionsEndpoint,
45+
getHeaders(),
46+
"",
47+
"locations"
48+
).then((r) => setLocations(r));
5549

56-
if (timeoutRef.current) {
57-
clearTimeout(timeoutRef.current);
58-
}
50+
loadDropdownOptionsPromise(
51+
optionsEndpoint,
52+
getHeaders(),
53+
"",
54+
"organizations"
55+
).then((r) => setOrganizations(r));
56+
}, []);
5957

60-
timeoutRef.current = setTimeout(() => {
61-
loadDropdownOptionsPromise(
62-
optionsEndpoint,
63-
getHeaders(),
64-
inputValue,
65-
type
66-
)
67-
.then((data) => resolve(data))
68-
.catch((reason) => reject(reason));
69-
}, 500);
70-
});
58+
const changeData = (field, newValue) => {
59+
handleSelect(field, newValue);
7160
};
7261

7362
return (
@@ -76,25 +65,22 @@ function PosterSubscriptionCriteria({
7665
<div className="mb-2">
7766
<div className="form-group">
7867
<label
79-
htmlFor="os2display-poster--select-subscription-places"
68+
htmlFor="select-subscription-places"
8069
className="form-label"
8170
>
8271
{t("filters-place")}
8372
</label>
84-
<AsyncSelect
73+
<MultiSelect
74+
isCreatable={false}
8575
id="subscription-search-place"
86-
isClearable
87-
isSearchable
88-
defaultOptions
89-
isMulti
90-
placeholder={t("single-search-placeholder")}
91-
loadOptions={(inputValue) =>
92-
debounceOptions(inputValue, "locations")
93-
}
94-
value={subscriptionPlaceValue}
95-
onChange={(newValue) => {
96-
handleSelect("subscriptionPlaceValue", newValue);
97-
}}
76+
label={t("subscription-search-select")}
77+
name="subscriptionPlaceValue"
78+
options={locations}
79+
hasSelectAll={false}
80+
onChange={(newValue) => handleSelect('subscriptionPlaceValue', newValue)}
81+
value={subscriptionPlaceValue ?? []}
82+
placeholder={t("subscription-search-placeholder")}
83+
labelledBy="select-subscription-places"
9884
/>
9985
</div>
10086
</div>
@@ -107,20 +93,17 @@ function PosterSubscriptionCriteria({
10793
>
10894
{t("filters-organizer")}
10995
</label>
110-
<AsyncSelect
111-
id="subscription-search-place"
112-
isClearable
113-
isSearchable
114-
defaultOptions
115-
isMulti
116-
placeholder={t("single-search-placeholder")}
117-
loadOptions={(inputValue) =>
118-
debounceOptions(inputValue, "organizations")
119-
}
120-
value={subscriptionOrganizerValue}
121-
onChange={(newValue) => {
122-
handleSelect("subscriptionOrganizerValue", newValue);
123-
}}
96+
<MultiSelect
97+
isCreatable={false}
98+
id="subscription-search-organizer"
99+
label={t("subscription-search-select")}
100+
name="subscriptionOrganizerValue"
101+
options={organizations}
102+
hasSelectAll={false}
103+
onChange={(newValue) => handleSelect('subscriptionOrganizerValue', newValue[0])}
104+
value={subscriptionOrganizerValue ?? []}
105+
placeholder={t("subscription-search-placeholder")}
106+
labelledBy="select-subscription-organizers"
124107
/>
125108
</div>
126109
</div>
@@ -133,18 +116,17 @@ function PosterSubscriptionCriteria({
133116
>
134117
{t("filters-tag")}
135118
</label>
136-
<AsyncSelect
137-
id="subscription-search-tag"
138-
isClearable
139-
isSearchable
140-
defaultOptions
141-
isMulti
142-
placeholder={t("single-search-placeholder")}
143-
loadOptions={(inputValue) => debounceOptions(inputValue, "tags")}
144-
value={subscriptionTagValue}
145-
onChange={(newValue) => {
146-
handleSelect("subscriptionTagValue", newValue);
147-
}}
119+
<MultiSelect
120+
isCreatable={false}
121+
id="subscription-search-tags"
122+
label={t("subscription-search-select")}
123+
name="subscriptionTagValue"
124+
options={tags}
125+
hasSelectAll={false}
126+
onChange={(newValue) => handleSelect('subscriptionTagValue', newValue)}
127+
value={subscriptionTagValue ?? []}
128+
placeholder={t("subscription-search-placeholder")}
129+
labelledBy="select-subscription-tags"
148130
/>
149131
</div>
150132
</div>

0 commit comments

Comments
 (0)