Skip to content

Commit

Permalink
feat: add organization filter to event and registration search pages
Browse files Browse the repository at this point in the history
  • Loading branch information
jorilindell committed May 16, 2024
1 parent 5c245cb commit 697a72a
Show file tree
Hide file tree
Showing 37 changed files with 448 additions and 53 deletions.
1 change: 1 addition & 0 deletions schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ module.exports = buildSchema(/* GraphQL */ `
include: [String]
page: Int
pageSize: Int
publisher: [String]
text: String
): RegistrationsResponse!
signup(id: ID!): Signup!
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from 'react';
import { useTranslation } from 'react-i18next';

import useOrganizationOptions from '../../../../domain/organization/hooks/useOrganizationOptions';
import getValue from '../../../../utils/getValue';
import FilterTag, { FilterTagProps } from '../FilterTag';

type Props = Omit<FilterTagProps, 'text' | 'type'>;

const PublisherFilterTag: React.FC<Props> = ({ value, ...rest }) => {
const { t } = useTranslation();
const { loading, options } = useOrganizationOptions();

const name = getValue(options.find((o) => o.value === value)?.label, '');

return (
<FilterTag
{...rest}
text={loading ? t('common.loading') : name}
type="publisher"
value={value}
/>
);
};

export default PublisherFilterTag;
39 changes: 23 additions & 16 deletions src/common/components/publisherSelector/PublisherSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,19 @@
import React, { useMemo } from 'react';
import { useTranslation } from 'react-i18next';

import { organizationPathBuilder } from '../../../domain/organization/utils';
import {
getOrganizationOption,
organizationPathBuilder,
} from '../../../domain/organization/utils';
import useUser from '../../../domain/user/hooks/useUser';
import useUserOrganizations from '../../../domain/user/hooks/useUserOrganizations';
import {
OrganizationFieldsFragment,
useOrganizationQuery,
} from '../../../generated/graphql';
import { useOrganizationQuery } from '../../../generated/graphql';
import useLocale from '../../../hooks/useLocale';
import { OptionType } from '../../../types';
import getPathBuilder from '../../../utils/getPathBuilder';
import getValue from '../../../utils/getValue';
import Combobox, { SingleComboboxProps } from '../combobox/Combobox';

const getOption = (organization: OrganizationFieldsFragment): OptionType => {
return {
label: getValue(organization.name, ''),
value: getValue(organization.id, ''),
};
};

export type PublisherSelectorProps = {
publisher?: string | null;
} & Omit<SingleComboboxProps<string | null>, 'toggleButtonAriaLabel'>;
Expand All @@ -33,6 +27,7 @@ const PublisherSelector: React.FC<PublisherSelectorProps> = ({
...rest
}) => {
const { t } = useTranslation();
const locale = useLocale();
const { user } = useUser();
const { loading, organizations } = useUserOrganizations(user);

Expand All @@ -48,17 +43,29 @@ const PublisherSelector: React.FC<PublisherSelectorProps> = ({
const selectedOrganization = React.useMemo(
() =>
organizationData?.organization
? getOption(organizationData.organization)
? getOrganizationOption({
idPath: 'id',
locale,
organization: organizationData.organization,
t,
})
: null,
[organizationData]
[locale, organizationData?.organization, t]
);

const options = useMemo(() => {
if (publisher) {
return selectedOrganization ? [selectedOrganization] : [];
}
return organizations.map((org) => getOption(org));
}, [organizations, publisher, selectedOrganization]);
return organizations.map((org) =>
getOrganizationOption({
idPath: 'id',
locale,
organization: org,
t,
})
);
}, [locale, organizations, publisher, selectedOrganization, t]);

return (
<Combobox
Expand Down
8 changes: 5 additions & 3 deletions src/common/components/searchPanel/SearchPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export type SearchRowProps = {
searchInputLabel: string;
searchInputPlaceholder: string;
searchInputValue: string;
selector?: ReactElement;
selectors?: ReactElement[];
};

const SearchRow: FC<SearchRowProps> = ({
Expand All @@ -25,11 +25,13 @@ const SearchRow: FC<SearchRowProps> = ({
searchInputLabel,
searchInputPlaceholder,
searchInputValue,
selector,
selectors,
}) => {
return (
<div className={styles.searchRow}>
{selector && <SelectorColumn>{selector}</SelectorColumn>}
{selectors?.map((selector, index) => (
<SelectorColumn key={index}>{selector}</SelectorColumn>
))}
<TextSearchColumn>
<SearchInput
className={searchInputClassName}
Expand Down
2 changes: 1 addition & 1 deletion src/common/components/searchPanel/searchPanel.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
margin-top: var(--spacing-m);
margin-bottom: var(--spacing-m);

@include medium-up {
@include large-up {
display: flex;
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/domain/app/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1111,6 +1111,7 @@
"buttonSearch": "Search events",
"labelEventType": "Type",
"labelPlace": "Search venue",
"labelPublisher": "Search publisher",
"labelSearch": "Search Linked Events",
"placeholderSearch": "Search events"
}
Expand Down Expand Up @@ -1952,6 +1953,7 @@
"buttonSearch": "Search",
"labelEventType": "Type",
"labelPlace": "Search venue",
"labelPublisher": "Search publisher",
"labelSearch": "Search registrations",
"placeholderSearch": "Search registrations"
},
Expand Down
2 changes: 2 additions & 0 deletions src/domain/app/i18n/fi.json
Original file line number Diff line number Diff line change
Expand Up @@ -1204,6 +1204,7 @@
"buttonSearch": "Etsi tapahtumia",
"labelEventType": "Tyyppi",
"labelPlace": "Etsi tapahtumapaikkaa",
"labelPublisher": "Etsi julkaisijaa",
"labelSearch": "Hae Linked Events -rajapinnasta",
"placeholderSearch": "Hae tapahtumia"
}
Expand Down Expand Up @@ -1952,6 +1953,7 @@
"buttonSearch": "Etsi",
"labelEventType": "Tyyppi",
"labelPlace": "Etsi tapahtumapaikkaa",
"labelPublisher": "Etsi julkaisijaa",
"labelSearch": "Hae ilmoittautumisia",
"placeholderSearch": "Hae ilmoittautumisia"
},
Expand Down
2 changes: 2 additions & 0 deletions src/domain/app/i18n/sv.json
Original file line number Diff line number Diff line change
Expand Up @@ -1111,6 +1111,7 @@
"buttonSearch": "Sök efter evenemang",
"labelEventType": "Typ",
"labelPlace": "Sök plats",
"labelPublisher": "Sök utgivare",
"labelSearch": "Sök efter evenemang från Linked Events",
"placeholderSearch": "Sök efter evenemang"
}
Expand Down Expand Up @@ -1952,6 +1953,7 @@
"buttonSearch": "Sök",
"labelEventType": "Typ",
"labelPlace": "Sök plats",
"labelPublisher": "Sök utgivare",
"labelSearch": "Sök registreringar",
"placeholderSearch": "Sök registreringar"
},
Expand Down
34 changes: 29 additions & 5 deletions src/domain/eventSearch/searchPanel/SearchPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { ClassNames } from '@emotion/react';
import { IconCalendar, IconHeart, IconLocation, Koros } from 'hds-react';
import {
IconCalendar,
IconGroup,
IconHeart,
IconLocation,
Koros,
} from 'hds-react';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { useLocation, useNavigate } from 'react-router';
Expand Down Expand Up @@ -28,11 +34,13 @@ import {
getEventSearchQuery,
} from '../../events/utils';
import PlaceSelector from './placeSelector/PlaceSelector';
import PublisherSelector from './publisherSelector/PublisherSelector';
import styles from './searchPanel.module.scss';

type SearchState = {
end: Date | null;
place: string[];
publisher: string[];
start: Date | null;
text: string;
type: EVENT_TYPE[];
Expand All @@ -50,6 +58,7 @@ const SearchPanel: React.FC = () => {
const [searchState, setSearchState] = useSearchState<SearchState>({
end: null,
place: [],
publisher: [],
start: null,
text: '',
type: [],
Expand All @@ -72,6 +81,12 @@ const SearchPanel: React.FC = () => {
});
};

const handleChangePublishers = (newPublishers: OptionType[]) => {
setSearchState({
publisher: newPublishers.map((p) => p.value),
});
};

const handleChangeText = (text: string) => {
setSearchState({ text });
};
Expand All @@ -88,10 +103,9 @@ const SearchPanel: React.FC = () => {
};

React.useEffect(() => {
const { end, places, start, text, types } = getEventSearchInitialValues(
location.search
);
setSearchState({ end, place: places, start, text, type: types });
const { end, places, publisher, start, text, types } =
getEventSearchInitialValues(location.search);
setSearchState({ end, place: places, publisher, start, text, type: types });
}, [location.search, setSearchState]);

return (
Expand Down Expand Up @@ -174,6 +188,16 @@ const SearchPanel: React.FC = () => {
}
/>
</div>
<div>
<PublisherSelector
icon={<IconGroup aria-hidden />}
onChange={handleChangePublishers}
toggleButtonLabel={t(
'eventSearchPage.searchPanel.labelPublisher'
)}
value={searchState.publisher}
/>
</div>
</div>
</div>
<div className={styles.buttonWrapper}>
Expand Down
23 changes: 21 additions & 2 deletions src/domain/eventSearch/searchPanel/__tests__/SearchPanel.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ import {
userEvent,
waitFor,
} from '../../../../utils/testUtils';
import {
mockedOrganizationsResponse,
organizations,
} from '../../../organizations/__mocks__/organizationsPage';
import {
mockedPlaceResponse,
mockedPlacesResponse,
Expand All @@ -19,14 +23,19 @@ import SearchPanel from '../SearchPanel';

configure({ defaultHidden: true });

const mocks = [mockedPlacesResponse, mockedPlaceResponse];
const mocks = [
mockedOrganizationsResponse,
mockedPlacesResponse,
mockedPlaceResponse,
];

const getElement = (
key:
| 'dateSelectorButton'
| 'endDateInput'
| 'eventTypeSelectorButton'
| 'placeSelectorButton'
| 'publisherSelectorButton'
| 'searchInput'
| 'startDateInput'
) => {
Expand All @@ -39,6 +48,8 @@ const getElement = (
return screen.getByRole('button', { name: 'Tyyppi' });
case 'placeSelectorButton':
return screen.getByRole('button', { name: 'Etsi tapahtumapaikkaa' });
case 'publisherSelectorButton':
return screen.getByRole('button', { name: 'Etsi julkaisijaa' });
case 'searchInput':
return screen.getByRole('textbox', {
name: 'Hae Linked Events -rajapinnasta',
Expand Down Expand Up @@ -92,6 +103,14 @@ test('should search events with correct search params', async () => {
const placeCheckbox = screen.getByLabelText(placeName);
await user.click(placeCheckbox);

// Publisher filtering
const publisherSelectorButton = getElement('publisherSelectorButton');
await user.click(publisherSelectorButton);
const publisherCheckbox = screen.getByLabelText(
organizations.data[0]?.name as string
);
await user.click(publisherCheckbox);

// Event type filtering
const eventTypeSelectorButton = getElement('eventTypeSelectorButton');
await user.click(eventTypeSelectorButton);
Expand All @@ -104,6 +123,6 @@ test('should search events with correct search params', async () => {
await user.click(searchButton);
await waitFor(() => expect(history.location.pathname).toBe('/fi/search'));
expect(history.location.search).toBe(
'?place=place%3A1&text=search&type=general&end=2021-03-12&start=2021-03-05'
'?place=place%3A1&publisher=organization%3A1&text=search&type=general&end=2021-03-12&start=2021-03-05'
);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React from 'react';

import MultiSelectDropdown, {
MultiselectDropdownProps,
} from '../../../../common/components/multiSelectDropdown/MultiSelectDropdown';
import skipFalsyType from '../../../../utils/skipFalsyType';
import useOrganizationOptions from '../../../organization/hooks/useOrganizationOptions';

export type PublisherSelectorProps = { value: string[] } & Omit<
MultiselectDropdownProps,
'options' | 'value'
>;

const PublisherSelector: React.FC<PublisherSelectorProps> = ({
id,
toggleButtonLabel,
value,
...rest
}) => {
const [searchValue, setSearchValue] = React.useState('');

const { loading, options } = useOrganizationOptions();

return (
<MultiSelectDropdown
{...rest}
id={id}
options={options}
searchValue={searchValue}
setSearchValue={setSearchValue}
showSearch={true}
showLoadingSpinner={loading}
toggleButtonLabel={toggleButtonLabel}
value={value
.map((v) => options.find((o) => o.value === v))
.filter(skipFalsyType)}
/>
);
};

export default PublisherSelector;
Loading

0 comments on commit 697a72a

Please sign in to comment.