Skip to content

Commit

Permalink
feat: add organization filter to event search page
Browse files Browse the repository at this point in the history
  • Loading branch information
jorilindell committed May 15, 2024
1 parent 6d89bdc commit edb4ab4
Show file tree
Hide file tree
Showing 21 changed files with 286 additions and 31 deletions.
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('id');

const name = getValue(options.find((o) => o.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
1 change: 1 addition & 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
1 change: 1 addition & 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
1 change: 1 addition & 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
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
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('id');

console.log(value);
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;
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React from 'react';

import {
configure,
render,
screen,
userEvent,
} from '../../../../../utils/testUtils';
import {
mockedOrganizationsResponse,
organizations,
organizationsOverrides,
} from '../../../../organizations/__mocks__/organizationsPage';
import PublisherSelector, {
PublisherSelectorProps,
} from '../PublisherSelector';

configure({ defaultHidden: true });

const mocks = [mockedOrganizationsResponse];

const toggleButtonLabel = 'Select place';

const defaultProps: PublisherSelectorProps = {
onChange: vi.fn(),
toggleButtonLabel,
value: [],
};

const renderComponent = (props?: Partial<PublisherSelectorProps>) =>
render(<PublisherSelector {...defaultProps} {...props} />, { mocks });

const getToggleButton = () =>
screen.getByRole('button', { name: toggleButtonLabel });

test('should render publisher selector', async () => {
const publisherId = organizations.data[0]?.id as string;
const publisherName = organizations.data[0]?.name as string;
const user = userEvent.setup();
renderComponent({ value: [publisherId] });

await screen.findByText(publisherName);

const toggleButton = getToggleButton();
await user.click(toggleButton);

for (const { name } of organizationsOverrides) {
await screen.findByLabelText(name as string);
}
});
1 change: 1 addition & 0 deletions src/domain/eventSearch/searchPanel/searchPanel.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
.searchPanel {
--search-panel-background-color: var(--color-coat-of-arms);
--search-panel-label-color: var(--color-white);
--search-panel-button-border-color: var(--color-white);
--search-panel-koros-height: 70px;

--search-panel-button-background-color: var(--color-coat-of-arms);
Expand Down
5 changes: 5 additions & 0 deletions src/domain/events/__tests__/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ describe('getEventsQueryVariables', () => {
location: [],
page: 1,
pageSize: 10,
publisher: [],
sort: DEFAULT_EVENT_SORT,
start: null,
text: '',
Expand All @@ -184,6 +185,10 @@ describe('getEventsQueryVariables', () => {
'?place=place:1&place=place:2',
{ ...defaultVariables, location: ['place:1', 'place:2'] },
],
[
'?publisher=publisher:1&publisher=publisher:2',
{ ...defaultVariables, publisher: ['publisher:1', 'publisher:2'] },
],
['?sort=name', { ...defaultVariables, sort: 'name' }],
['?start=2021-05-27', { ...defaultVariables, start: '2021-05-27' }],
['?text=search', { ...defaultVariables, text: 'search' }],
Expand Down
1 change: 1 addition & 0 deletions src/domain/events/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export enum EVENT_SEARCH_PARAMS {
END = 'end',
PAGE = 'page',
PLACE = 'place',
PUBLISHER = 'publisher',
RETURN_PATH = 'returnPath',
SORT = 'sort',
START = 'start',
Expand Down
5 changes: 4 additions & 1 deletion src/domain/events/eventList/EventList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,10 @@ const EventListContainer: React.FC<EventListContainerProps> = (props) => {

const variables = {
...baseVariables,
...getEventsQueryVariables(location.search, baseVariables),
...omit(
getEventsQueryVariables(location.search, baseVariables),
'publisher'
),
};

const { data: eventsData, loading } = useEventsQuery({
Expand Down
1 change: 1 addition & 0 deletions src/domain/events/eventList/__tests__/EventList.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const variables = {
eventType: [],
include: EVENT_LIST_INCLUDES,
location: [],
publisher: [],
start: null,
text: '',
};
Expand Down
Loading

0 comments on commit edb4ab4

Please sign in to comment.