Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UHF-9576 linked events keywords #920

Merged
merged 5 commits into from
Mar 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion dist/css/styles.min.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/js/linkedevents.min.js

Large diffs are not rendered by default.

15 changes: 14 additions & 1 deletion hdbt.theme
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use Drupal\helfi_tpr\Entity\Unit;
use Drupal\image\Entity\ImageStyle;
use Drupal\image\Plugin\Field\FieldType\ImageItem;
use Drupal\language\ConfigurableLanguageManagerInterface;
use Drupal\link\LinkItemInterface;
use Drupal\menu_link_content\Plugin\Menu\MenuLinkContent;
use Drupal\node\Entity\Node;
use Drupal\node\NodeInterface;
Expand Down Expand Up @@ -1629,6 +1630,7 @@ function hdbt_preprocess_pager(&$variables): void {
function hdbt_preprocess_paragraph__event_list(&$variables): void {
// Expose event list variables to frontend.
if (isset($variables['paragraph'])) {
/** @var \Drupal\helfi_react_search\Entity\EventList $paragraph */
$paragraph = $variables['paragraph'];
$settings = [];

Expand All @@ -1643,9 +1645,20 @@ function hdbt_preprocess_paragraph__event_list(&$variables): void {
$settings['field_event_count'] = '3';
}

$langcode = \Drupal::languageManager()
->getCurrentLanguage()
->getId();

$settings['field_filter_keywords'] = array_map(static fn ($term) => [
'id' => $term->get('field_keyword_id')->getString(),
'name' => $term->getName(),
], $paragraph->getFilterKeywords($langcode));

if ($paragraph->hasField('field_api_url') && !$paragraph->get('field_api_url')->isEmpty()) {
$linkedEvents = Drupal::service('helfi_react_search_linked_events');
$events_public_url = $paragraph->get('field_api_url')->first()->getUrl()->toString();
$link_field = $events_public_url = $paragraph->get('field_api_url')->first();
assert($link_field instanceof LinkItemInterface);
$events_public_url = $link_field->getUrl()->toString();
$settings['events_public_url'] = $events_public_url;
$params = $linkedEvents->parseParams($events_public_url);
$eventUrl = $linkedEvents->getEventsRequest($params, $settings['field_event_count']);
Expand Down
44 changes: 44 additions & 0 deletions src/js/react/apps/linkedevents/components/TopicsFilter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Select } from 'hds-react';
hyrsky marked this conversation as resolved.
Show resolved Hide resolved
import { useAtomValue, useAtom, useSetAtom } from 'jotai';

import { topicsAtom, topicSelectionAtom, updateParamsAtom} from '../store';
import SearchComponents from '../enum/SearchComponents';
import ApiKeys from '../enum/ApiKeys';

function TopicsFilter() {
const topics = useAtomValue(topicsAtom);
const [topicSelection, setTopicsFilter] = useAtom(topicSelectionAtom);
const updateParams = useSetAtom(updateParamsAtom);

const onChange = (value: any) => {
setTopicsFilter(value);
updateParams({ [ApiKeys.KEYWORDS]: value.map((topic: any) => topic.value).join(',') });
};

const selectLabel: string = Drupal.t('Event topic', {}, { context: 'React search: topics filter' });

return (
<div className='hdbt-search__filter event-form__filter--topics'>
<Select
className='hdbt-search__dropdown'
clearButtonAriaLabel={Drupal.t('Clear @label selection', {'@label': selectLabel}, { context: 'React search clear selection label' })}
label={selectLabel}
multiselect
// @ts-ignore
options={topics}
value={topicSelection}
id={SearchComponents.TOPICS}
onChange={onChange}
placeholder={Drupal.t('All topics', {}, { context: 'React search: topics filter' })}
selectedItemRemoveButtonAriaLabel={Drupal.t('Remove item', {}, { context: 'React search: remove item aria label' })}
theme={{
'--focus-outline-color': 'var(--hdbt-color-black)',
'--multiselect-checkbox-background-selected': 'var(--hdbt-color-black)',
'--placeholder-color': 'var(--hdbt-color-black)',
}}
/>
</div>
);
}

export default TopicsFilter;
21 changes: 16 additions & 5 deletions src/js/react/apps/linkedevents/containers/FormContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
formErrorsAtom,
updateUrlAtom,
} from '../store';
import TopicsFilter from '../components/TopicsFilter';


function FormContainer() {
Expand All @@ -24,15 +25,22 @@ function FormContainer() {
const errors = useAtomValue(formErrorsAtom);
const url = useAtomValue(urlAtom);
const updateUrl = useSetAtom(updateUrlAtom);
const { showLocation, showFreeFilter, showRemoteFilter, showTimeFilter } = filterSettings;
const {
showLocation,
showFreeFilter,
showRemoteFilter,
showTimeFilter,
showTopicsFilter,
} = filterSettings;

const onSubmit = () => {
updateUrl();
};

const handleSubmit = (e: FormEvent) => {
e.preventDefault();
onSubmit(); return false;
onSubmit();
return false;
};

const bothCheckboxes = showFreeFilter && showRemoteFilter;
Expand All @@ -42,7 +50,7 @@ function FormContainer() {
const freeLabel = bothCheckboxes ? freeTranslation : `${showOnlyLabel} ${freeTranslation.toLowerCase()}`;
const remoteLabel = bothCheckboxes ? remoteTranslation : `${showOnlyLabel} ${remoteTranslation.toLowerCase()}`;

const showForm = showLocation || showFreeFilter || showTimeFilter || showRemoteFilter;
const showForm = showLocation || showFreeFilter || showTimeFilter || showRemoteFilter || showTopicsFilter;
const HeadingTag = eventListTitle ? 'h3' : 'h2';

if (!showForm) {
Expand All @@ -54,14 +62,17 @@ function FormContainer() {
<HeadingTag className='event-list__filter-title'>{Drupal.t('Filter events', {}, { context: 'Events search: search form title' })}</HeadingTag>
<div className='event-form__filters-container'>
<div className='event-form__filter-section-container'>
{
showTopicsFilter &&
<TopicsFilter />
}
{
showLocation &&
<LocationFilter />
}
{
showTimeFilter &&
<DateSelect
/>
<DateSelect />
}
</div>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import FilterButton from '@/react/common/FilterButton';
import {
resetFormAtom,
locationSelectionAtom,
topicSelectionAtom,
freeFilterAtom,
remoteFilterAtom,
startDateAtom,
Expand All @@ -29,16 +30,23 @@ const SelectionsContainer = ({ url }: SelectionsContainerProps) => {
const startDate = useAtomValue(startDateAtom);
const endDate = useAtomValue(endDateAtom);
const [locationSelection, setLocationSelection] = useAtom(locationSelectionAtom);
const [topicsSelection, setTopicsSelection] = useAtom(topicSelectionAtom);
const resetForm = useSetAtom(resetFormAtom);

const showClearButton = locationSelection.length || startDate || endDate || freeFilter || remoteFilter;
const showClearButton = locationSelection.length || topicsSelection.length || startDate || endDate || freeFilter || remoteFilter;

if (!url) {
return null;
}

return (
<FilterBulletsWrapper showClearButton={showClearButton} resetForm={resetForm} url={url}>
<ListFilterPills
updater={setTopicsSelection}
valueKey={ApiKeys.KEYWORDS}
values={topicsSelection}
url={url}
/>
<ListFilterPills
updater={setLocationSelection}
valueKey={ApiKeys.LOCATION}
Expand Down
1 change: 1 addition & 0 deletions src/js/react/apps/linkedevents/enum/ApiKeys.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export const ApiKeys = {
KEYWORDS: 'keyword_OR',
FREE: 'is_free',
LOCATION: 'location',
PAGESIZE: 'page_size',
Expand Down
1 change: 1 addition & 0 deletions src/js/react/apps/linkedevents/enum/SearchComponents.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const SearchComponents = {
TOPICS: 'topics',
LOCATION: 'location',
DATE: 'date',
END_DISABLED: 'end_disabled',
Expand Down
16 changes: 16 additions & 0 deletions src/js/react/apps/linkedevents/store.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Location from './types/Location';
import OptionType from './types/OptionType';
import FormErrors from './types/FormErrors';
import ApiKeys from './enum/ApiKeys';
import Topic from './types/Topic';

interface Options {
[key: string]: string
Expand Down Expand Up @@ -54,9 +55,14 @@ const createBaseAtom = () => {
showTimeFilter: settings?.field_event_time,
showFreeFilter: settings?.field_free_events,
showRemoteFilter: settings?.field_remote_events,
showTopicsFilter: settings?.field_filter_keywords.length > 0,
eventCount: Number(settings?.field_event_count)
};
const locations = transformLocations(settings?.places);
const topics: Topic[] = settings?.field_filter_keywords.map(topic => ({
value: topic.id,
label: topic.name.charAt(0).toUpperCase() + topic.name.slice(1),
}));

let baseUrl;
let initialParams;
Expand All @@ -76,6 +82,7 @@ const createBaseAtom = () => {
initialUrl: eventsApiUrl,
initialParams,
locations,
topics,
eventListTitle,
eventsPublicUrl,
};
Expand All @@ -101,6 +108,10 @@ export const locationAtom = atom(
(get) => get(baseAtom)?.locations
);

export const topicsAtom = atom(
(get) => get(baseAtom)?.topics
);

export const titleAtom = atom(
(get) => get(baseAtom)?.eventListTitle
);
Expand All @@ -115,6 +126,8 @@ export const settingsAtom = atom(
showLocation: false,
showRemoteFilter: false,
showTimeFilter: false,
showTopicsFilter: false,
topics: [],
eventCount: 3
}
);
Expand All @@ -127,6 +140,8 @@ export const paramsAtom = atom(new URLSearchParams());

export const locationSelectionAtom = atom<OptionType[]>([] as OptionType[]);

export const topicSelectionAtom = atom<Topic[]>([]);

export const startDateAtom = atom<DateTime|undefined>(undefined);

export const endDateAtom = atom<DateTime|undefined>(undefined);
Expand All @@ -146,6 +161,7 @@ export const resetFormAtom = atom(null, (get, set) => {
const initialParams = get(initialParamsAtom);

set(locationSelectionAtom, []);
set(topicSelectionAtom, []);
set(startDateAtom, undefined);
set(endDateAtom, undefined);
set(endDisabledAtom, false);
Expand Down
3 changes: 3 additions & 0 deletions src/js/react/apps/linkedevents/types/FilterSettings.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import Topic from './Topic';

type FilterSettings = {
showLocation: boolean,
showTimeFilter: boolean,
showFreeFilter: boolean,
showRemoteFilter: boolean,
showTopicsFilter: boolean,
eventCount: number
};

Expand Down
7 changes: 7 additions & 0 deletions src/js/react/apps/linkedevents/types/Topic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

export type Topic = {
value: string,
label: string
};

export default Topic;
4 changes: 4 additions & 0 deletions src/js/types/drupalSettings.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ declare namespace drupalSettings {
field_free_events: boolean,
field_remote_events: boolean,
field_event_count: string,
field_filter_keywords: {
id: string,
name: string,
}[],
places: {
[key:string]: {
id: string,
Expand Down
1 change: 1 addition & 0 deletions src/scss/06_components/paragraphs/_event-list.scss
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ $tag-vertical-padding: 5px;
}

.event-form__filter--location,
.event-form__filter--topics,
.event-form__filter--date {
flex-basis: 100%;

Expand Down
Loading