Skip to content

Commit 9e8b8cc

Browse files
committed
Merge branch 'unify-search-field' of Arnei/opencast-admin-interface into r/17.x
Pull request #1337 Fixes #1269 Unify search container
2 parents fbf2f9d + a354bbb commit 9e8b8cc

7 files changed

Lines changed: 170 additions & 112 deletions

File tree

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { t } from "i18next";
2+
import ButtonLikeAnchor from "./ButtonLikeAnchor";
3+
4+
5+
const SearchContainer = ({
6+
value,
7+
handleChange,
8+
clearSearchField,
9+
isExpand,
10+
isInModal,
11+
isDisabled,
12+
style,
13+
}: {
14+
value: string
15+
handleChange: (value: string) => unknown
16+
clearSearchField: () => unknown
17+
isExpand?: boolean
18+
isInModal?: boolean
19+
isDisabled?: boolean
20+
style?: object
21+
}) => {
22+
23+
let containerClassName = "search-container";
24+
if (isExpand) {
25+
containerClassName = containerClassName + " expand";
26+
}
27+
if (isInModal) {
28+
containerClassName = containerClassName + " modal";
29+
}
30+
let inputClassName = "search";
31+
if (isDisabled) {
32+
inputClassName = inputClassName + " disabled"
33+
}
34+
if (!value) {
35+
inputClassName = inputClassName + " fullwidth";
36+
}
37+
let buttonClassName = "clear";
38+
if (isDisabled) {
39+
buttonClassName = buttonClassName + " disabled"
40+
}
41+
42+
return (
43+
<div className={containerClassName} style={style}>
44+
{value && <ButtonLikeAnchor
45+
extraClassName={buttonClassName}
46+
onClick={() => clearSearchField()}
47+
disabled={isDisabled}
48+
/>}
49+
<input
50+
type="text"
51+
className={inputClassName}
52+
placeholder={t("TABLE_FILTERS.PLACEHOLDER")}
53+
onChange={(e) => handleChange(e.target.value)}
54+
name="textFilter"
55+
value={value}
56+
disabled={isDisabled}
57+
/>
58+
</div>
59+
);
60+
};
61+
62+
export default SearchContainer;

src/components/shared/TableFilters.tsx

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import DropDown from "./DropDown";
3131
import { AsyncThunk } from "@reduxjs/toolkit";
3232
import ButtonLikeAnchor from "./ButtonLikeAnchor";
3333
import { ParseKeys } from "i18next";
34+
import SearchContainer from "./SearchContainer";
3435
import { Resource } from "../../slices/tableSlice";
3536

3637
/**
@@ -99,6 +100,14 @@ const TableFilters = ({
99100
dispatch(loadResourceIntoTable());
100101
};
101102

103+
const handleSearchChange = (value: string) => {
104+
handleChange("textFilter", value);
105+
}
106+
107+
const clearSearchField = () => {
108+
dispatch(removeTextFilter());
109+
};
110+
102111
// Handle changes when an item of the component is changed
103112
const handleChange = (name: string, value: string) => {
104113
let mustApplyChanges = false;
@@ -235,16 +244,12 @@ const TableFilters = ({
235244
<>
236245
<div className="filters-container">
237246
{/* Text filter - Search Query */}
238-
<div className="search-container">
239-
<input
240-
type="text"
241-
className="search expand"
242-
placeholder={t("TABLE_FILTERS.PLACEHOLDER")}
243-
onChange={(e) => handleChange("textFilter", e.target.value)}
244-
name="textFilter"
245-
value={textFilter}
246-
/>
247-
</div>
247+
<SearchContainer
248+
value={textFilter}
249+
handleChange={handleSearchChange}
250+
clearSearchField={clearSearchField}
251+
isExpand={true}
252+
/>
248253

249254
{/* Selection of filters and management of filter profiles*/}
250255
{/*show only if filters.filters contains filters*/}
@@ -348,12 +353,14 @@ const TableFilters = ({
348353
</div>
349354

350355
{/* Remove icon to clear all filters */}
351-
<ButtonLikeAnchor
352-
onClick={removeFilters}
353-
tooltipText="TABLE_FILTERS.CLEAR"
354-
>
355-
<i className="clear fa fa-times" />
356-
</ButtonLikeAnchor>
356+
{filterMap.some(e => e.value) &&
357+
<ButtonLikeAnchor
358+
onClick={removeFilters}
359+
tooltipText="TABLE_FILTERS.CLEAR"
360+
>
361+
<i className="clear fa fa-times" />
362+
</ButtonLikeAnchor>
363+
}
357364
{/* Settings icon to open filters profile dialog (save and editing filter profiles)*/}
358365
<ButtonLikeAnchor
359366
onClick={() => setFilterSettings(!showFilterSettings)}

src/components/shared/wizard/SelectContainer.tsx

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import cn from "classnames";
44
import { useField } from "formik";
55
import ButtonLikeAnchor from "../ButtonLikeAnchor";
66
import { ParseKeys } from "i18next";
7+
import SearchContainer from "../SearchContainer";
78

89
type Item = {
910
name: string
@@ -67,10 +68,6 @@ const SelectContainer = ({
6768
// eslint-disable-next-line react-hooks/exhaustive-deps
6869
}, []);
6970

70-
const disabledStyle = {
71-
backgroundColor: "#eeeff0",
72-
};
73-
7471
const disabledSelectStyle = {
7572
backgroundColor: "#eeeff0",
7673
};
@@ -205,20 +202,13 @@ const SelectContainer = ({
205202
</label>
206203
{/*Search*/}
207204
{resource.searchable && (
208-
<div className="search-container">
209-
{/* search bar */}
210-
<ButtonLikeAnchor extraClassName="clear" onClick={() => clearSearchField()} />
211-
<input
212-
type="text"
213-
id="search"
214-
className="search"
215-
disabled={!manageable}
216-
style={manageable ? {} : disabledStyle}
217-
placeholder={t("TABLE_FILTERS.PLACEHOLDER")}
218-
onChange={(e) => handleChangeSearch(e.target.value)}
219-
value={searchField}
220-
/>
221-
</div>
205+
<SearchContainer
206+
value={searchField}
207+
handleChange={handleChangeSearch}
208+
clearSearchField={clearSearchField}
209+
isDisabled={!manageable}
210+
style={{ marginTop: "10px" }}
211+
/>
222212
)}
223213
{/*Select with options provided by backend*/}
224214
<select

src/components/users/partials/wizard/UserEffectiveRolesTab.tsx

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import React, { useState } from "react";
33
import { useTranslation } from "react-i18next";
44
import ButtonLikeAnchor from "../../../shared/ButtonLikeAnchor";
55
import ModalContent from "../../../shared/modals/ModalContent";
6+
import SearchContainer from "../../../shared/SearchContainer";
67

78
/**
89
* This component renders the effective role tab of the user details modal
@@ -44,17 +45,11 @@ const UserEffectiveRolesTab = <T extends RequiredFormProps>({
4445
<p>{t("USERS.USERS.DETAILS.DESCRIPTION.EFFECTIVEROLES")}</p>
4546

4647
{/* list all roles a user got */}
47-
<div className="search-container">
48-
<ButtonLikeAnchor extraClassName="clear" onClick={() => clearSearchField()} />
49-
<input
50-
type="text"
51-
id="search_effective"
52-
className="search"
53-
value={searchField}
54-
onChange={(e) => handleChangeSearch(e.target.value)}
55-
placeholder={t("TABLE_FILTERS.PLACEHOLDER")}
56-
/>
57-
</div>
48+
<SearchContainer
49+
value={searchField}
50+
handleChange={handleChangeSearch}
51+
clearSearchField={clearSearchField}
52+
/>
5853

5954
<select multiple style={{ height: "26em" }}>
6055
{items.map((item, key) => (

src/styles/components/_inputs.scss

Lines changed: 71 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
@use "../base/fontawesome/fa-mixins";
33
@use "../base/fontawesome/variables" as variables2;
44
@use "../base/variables";
5+
@use "../mixins/button";
56

67
/**
78
* Licensed to The Apereo Foundation under one or more contributor license
@@ -82,21 +83,6 @@ textarea {
8283
background-position: 19px center;
8384
}
8485

85-
&.search,
86-
&#search {
87-
appearance: none;
88-
vertical-align: top;
89-
padding: 0 20px 0 40px !important;
90-
height: 40px;
91-
92-
&.expand {
93-
transition: width 0.2s ease-in;
94-
95-
&:focus {
96-
width: 200px;
97-
}
98-
}
99-
}
10086

10187
&.small {
10288
padding: 15px;
@@ -177,14 +163,83 @@ input[type="radio"].ios {
177163
width: 100%;
178164
}
179165

166+
180167
.search-container {
181-
@include fa-mixins.fa-icon(variables2.$fa-var-search, after, inline, 0, 0, inherit, 13px);
168+
width: 100% !important;
169+
height: 40px;
182170
position: relative;
183171

172+
@include fa-mixins.fa-icon(variables2.$fa-var-search, after, inline, 0, 0, inherit, 13px);
173+
174+
outline: variables.$thin-border-stroke color.adjust(variables.$main-border-color, $lightness: -2%);
175+
border-radius: variables.$main-border-radius;
176+
177+
input.search {
178+
border: 0;
179+
border-right-width: 0;
180+
float: left;
181+
margin: 0;
182+
183+
appearance: none;
184+
vertical-align: top;
185+
padding: 0 0px 0 40px !important;
186+
height: 40px;
187+
188+
outline: none;
189+
190+
width: calc(100% - 46px) !important;
191+
192+
&.fullwidth {
193+
width: 100% !important;
194+
}
195+
}
196+
197+
198+
&:focus-within {
199+
outline: 3px solid #378dd4;
200+
}
201+
202+
&.expand {
203+
width: 170px !important;
204+
transition: width 0.2s ease-in;
205+
206+
&:focus-within {
207+
width: 240px !important;
208+
// width: calc(100% + 46px) !important;
209+
}
210+
}
211+
212+
button.clear {
213+
height: 40px;
214+
width: 46px;
215+
float: right;
216+
@include button.btn(white);
217+
@include fa-mixins.fa-icon(variables2.$fa-var-times, before, block, 0, 0.45em 0.45em 0.55em 0.45em, inherit, 14px);
218+
opacity: 1 !important;
219+
220+
&, &:hover, &:focus, &:active {
221+
background: none;
222+
background-color: #fff;
223+
border: 0;
224+
box-shadow: none;
225+
}
226+
227+
&.disabled, &.disabled:hover, &.disabled:focus, &.disabled:active {
228+
background-color: #eeeff0,
229+
}
230+
231+
&+input.search {
232+
width: calc(100% - 46px) !important;
233+
}
234+
235+
236+
}
237+
184238
&:after {
185239
position: absolute;
186240
top: 13px;
187241
left: 10px;
188242
}
243+
189244
}
190245

src/styles/components/_multi-select.scss

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -75,31 +75,6 @@
7575
display: block;
7676
float: left;
7777
vertical-align: top;
78-
79-
.search-container {
80-
width: 100% !important;
81-
margin-top: 10px;
82-
}
83-
84-
input.search {
85-
border-radius: variables.$main-border-radius 0 0 variables.$main-border-radius;
86-
border-right-width: 0;
87-
float: left;
88-
margin: 0;
89-
}
90-
91-
button.clear {
92-
height: inherit;
93-
float: right;
94-
@include button.btn(white);
95-
@include fa-mixins.fa-icon(variables2.$fa-var-times, before, block, 0, 0.45em 0.45em 0.55em 0.45em, inherit, 14px);
96-
97-
border-radius: 0 variables.$main-border-radius variables.$main-border-radius 0;
98-
99-
&+input.search {
100-
width: calc(100% - 46px) !important;
101-
}
102-
}
10378
}
10479
}
10580

src/styles/components/modals/_modal-base.scss

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -136,32 +136,6 @@
136136

137137
.modal-body {
138138
padding: 3%;
139-
140-
.search-container {
141-
width: 100% !important;
142-
margin-top: 5px;
143-
}
144-
145-
input.search {
146-
border-radius: variables.$main-border-radius 0 0 variables.$main-border-radius;
147-
border-right-width: 0;
148-
float: left;
149-
margin: 0;
150-
}
151-
152-
button.clear {
153-
height: inherit;
154-
width: 46px;
155-
float: right;
156-
@include button.btn(white);
157-
@include fa-mixins.fa-icon(variables2.$fa-var-times, before, block, 0, 0.45em 0.45em 0.55em 0.45em, inherit, 14px);
158-
159-
border-radius: 0 variables.$main-border-radius variables.$main-border-radius 0;
160-
161-
&+input.search {
162-
width: calc(100% - 46px) !important;
163-
}
164-
}
165139
}
166140

167141
.modal {

0 commit comments

Comments
 (0)