Skip to content

Commit

Permalink
Make filters box collapsible in React competitions overview (thewca#1…
Browse files Browse the repository at this point in the history
…0461)

* Make filters box collapsible

* Move reset button into filter view

* Move filter open/close action to floated button

* Make ESLint happy

* Make time filter buttons more compact

* Turn year-gap into header in cell break lines
  • Loading branch information
gregorbg authored Jan 3, 2025
1 parent 2d174a1 commit ec318a2
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 93 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@ import UtcDatePicker from '../wca/UtcDatePicker';
function CompetitionsFilters({
filterState,
dispatchFilter,
displayMode,
setDisplayMode,
shouldShowRegStatus,
setShouldShowRegStatus,
shouldShowAdminDetails,
canViewAdminDetails,
}) {
Expand Down Expand Up @@ -53,32 +49,13 @@ function CompetitionsFilters({
<TimeOrderButtonGroup filterState={filterState} dispatchFilter={dispatchFilter} />
</Form.Field>

<Form.Group inline>
<CompDisplayCheckboxes
shouldIncludeCancelled={filterState.shouldIncludeCancelled}
dispatchFilter={dispatchFilter}
shouldShowRegStatus={shouldShowRegStatus}
setShouldShowRegStatus={setShouldShowRegStatus}
shouldShowAdminDetails={shouldShowAdminDetails}
canViewAdminDetails={canViewAdminDetails}
displayMode={displayMode}
/>
</Form.Group>

{canViewAdminDetails && shouldShowAdminDetails && (
<Form.Group>
<Form.Field>
<AdminStatusButtonGroup filterState={filterState} dispatchFilter={dispatchFilter} />
</Form.Field>
</Form.Group>
)}

<Form.Group>
<ToggleListOrMapDisplay
displayMode={displayMode}
setDisplayMode={setDisplayMode}
/>
</Form.Group>
</Form>
);
}
Expand Down Expand Up @@ -263,48 +240,39 @@ function TimeOrderButtonGroup({ filterState, dispatchFilter }) {
return (
<>
<label htmlFor="state">{I18n.t('competitions.index.state')}</label>
<Button.Group id="state" size="small">

<Button.Group id="state" size="small" compact primary>
<Button
primary
type="button"
name="state"
id="present"
value="present"
onClick={() => dispatchFilter({ timeOrder: 'present' })}
active={filterState.timeOrder === 'present'}
>
<span className="caption">{I18n.t('competitions.index.present')}</span>
{I18n.t('competitions.index.present')}
</Button>

<Button
primary
type="button"
name="state"
id="recent"
value="recent"
onClick={() => dispatchFilter({ timeOrder: 'recent' })}
active={filterState.timeOrder === 'recent'}
data-tooltip={I18n.t('competitions.index.tooltips.recent', { count: competitionConstants.competitionRecentDays })}
data-variation="tiny"
>
<span className="caption">{I18n.t('competitions.index.recent')}</span>
{I18n.t('competitions.index.recent')}
</Button>

<PastCompYearSelector filterState={filterState} dispatchFilter={dispatchFilter} />

<Button
primary
type="button"
name="state"
id="by_announcement"
value="by_announcement"
onClick={() => dispatchFilter({ timeOrder: 'by_announcement' })}
active={filterState.timeOrder === 'by_announcement'}
data-tooltip={I18n.t('competitions.index.sort_by_announcement')}
data-variation="tiny"
>
<span className="caption">{I18n.t('competitions.index.by_announcement')}</span>
{I18n.t('competitions.index.by_announcement')}
</Button>

<CustomDateSelector filterState={filterState} dispatchFilter={dispatchFilter} />
Expand All @@ -318,42 +286,39 @@ function AdminStatusButtonGroup({ filterState, dispatchFilter }) {
return (
<>
<label htmlFor="admin-status">{I18n.t('competitions.index.admin_status')}</label>
<Button.Group id="admin-status">
<Button.Group id="admin-status" compact>

<Button
primary
type="button"
name="admin-status"
id="all"
value="all"
onClick={() => dispatchFilter({ adminStatus: 'all' })}
active={filterState.adminStatus === 'all'}
>
<span className="caption">{I18n.t('competitions.index.status_flags.all')}</span>
{I18n.t('competitions.index.status_flags.all')}
</Button>

<Button
color="yellow"
type="button"
name="admin-status"
id="warning"
value="warning"
onClick={() => dispatchFilter({ adminStatus: 'warning' })}
active={filterState.adminStatus === 'warning'}
>
<span className="caption">{I18n.t('competitions.index.status_flags.warning')}</span>
{I18n.t('competitions.index.status_flags.warning')}
</Button>

<Button
negative
type="button"
name="admin-status"
id="danger"
value="danger"
onClick={() => dispatchFilter({ adminStatus: 'danger' })}
active={filterState.adminStatus === 'danger'}
>
<span className="caption">{I18n.t('competitions.index.status_flags.danger')}</span>
{I18n.t('competitions.index.status_flags.danger')}
</Button>

</Button.Group>
Expand All @@ -364,26 +329,24 @@ function AdminStatusButtonGroup({ filterState, dispatchFilter }) {
function PastCompYearSelector({ filterState, dispatchFilter }) {
return (
<Button
primary
type="button"
name="state"
id="past"
value="past"
onClick={() => dispatchFilter({ timeOrder: 'past' })}
active={filterState.timeOrder === 'past'}
>
<span className="caption">
{
// eslint-disable-next-line no-nested-ternary
filterState.timeOrder === 'past' ? (
filterState.selectedYear === 'all_years' ? I18n.t('competitions.index.past_all')
: I18n.t('competitions.index.past_from', { year: filterState.selectedYear })
) : I18n.t('competitions.index.past')
}
</span>
{
// eslint-disable-next-line no-nested-ternary
filterState.timeOrder === 'past' ? (
filterState.selectedYear === 'all_years' ? I18n.t('competitions.index.past_all')
: I18n.t('competitions.index.past_from', { year: filterState.selectedYear })
) : I18n.t('competitions.index.past')
}
<Dropdown
name="year"
id="year"
simple
compact
pointing
scrolling
upward={false}
Expand Down Expand Up @@ -414,15 +377,13 @@ function PastCompYearSelector({ filterState, dispatchFilter }) {
function CustomDateSelector({ filterState, dispatchFilter }) {
const customTimeSelectionButton = (
<Button
primary
type="button"
name="state"
id="custom"
value="custom"
onClick={() => dispatchFilter({ timeOrder: 'custom' })}
active={filterState.timeOrder === 'custom'}
>
<span className="caption">{I18n.t('competitions.index.custom')}</span>
{I18n.t('competitions.index.custom')}
</Button>
);

Expand Down Expand Up @@ -464,7 +425,7 @@ function CustomDateSelector({ filterState, dispatchFilter }) {
);
}

function CompDisplayCheckboxes({
export function CompDisplayCheckboxes({
shouldIncludeCancelled,
dispatchFilter,
shouldShowRegStatus,
Expand Down Expand Up @@ -520,7 +481,7 @@ function CompDisplayCheckboxes({
);
}

function ToggleListOrMapDisplay({ displayMode, setDisplayMode }) {
export function ToggleListOrMapDisplay({ displayMode, setDisplayMode }) {
return (
<Button.Group toggle fluid id="display">
<Button type="button" name="display" id="display-list" active={displayMode === 'list'} onClick={() => setDisplayMode('list')}>
Expand All @@ -535,21 +496,4 @@ function ToggleListOrMapDisplay({ displayMode, setDisplayMode }) {
);
}

export function ResetFilters({ dispatchFilter, floated = null }) {
return (
<Button
type="reset"
floated={floated}
size="mini"
id="reset"
icon
labelPosition="left"
onClick={() => dispatchFilter({ type: 'reset' })}
>
<Icon name="repeat" />
{I18n.t('competitions.index.reset_filters')}
</Button>
);
}

export default CompetitionsFilters;
86 changes: 70 additions & 16 deletions app/webpacker/components/CompetitionsOverview/CompetitionsView.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,21 @@ import React, {
useEffect, useMemo, useReducer, useState,
} from 'react';
import { keepPreviousData, useInfiniteQuery, useQuery } from '@tanstack/react-query';
import { Container, Header } from 'semantic-ui-react';
import {
Button,
Container,
Form,
Header,
Icon,
Segment,
Transition,
} from 'semantic-ui-react';

import I18n from '../../lib/i18n';
import { apiV0Urls, WCA_API_PAGINATION } from '../../lib/requests/routes.js.erb';
import { fetchJsonOrError } from '../../lib/requests/fetchWithAuthenticityToken';

import CompetitionsFilters, { ResetFilters } from './CompetitionsFilters';
import CompetitionsFilters, { CompDisplayCheckboxes, ToggleListOrMapDisplay } from './CompetitionsFilters';
import ListView from './ListView';
import MapView from './MapView';
import {
Expand Down Expand Up @@ -106,24 +114,70 @@ function CompetitionsView({ canViewAdminDetails = false }) {
})
) : baseCompetitions), [baseCompetitions, compRegistrationData, shouldShowRegStatus]);

const [showFilters, setShowFilters] = useState(true);

return (
<Container>
<Header as="h2">
<Button
floated="right"
icon
labelPosition="left"
toggle
// We want to make the button green to invite the user's attention
// when the filters are *not* currently shown. When the filters are shown,
// the button to disable/hide them should be "not-active-grey" to remove emphasis.
active={!showFilters}
onClick={() => setShowFilters((prev) => !prev)}
>
<Icon name="filter" />
{showFilters ? I18n.t('competitions.index.hide_filters') : I18n.t('competitions.index.show_filters')}
</Button>
{I18n.t('competitions.index.title')}
<ResetFilters dispatchFilter={dispatchFilter} floated="right" />
</Header>
<CompetitionsFilters
filterState={filterState}
dispatchFilter={dispatchFilter}
displayMode={displayMode}
setDisplayMode={setDisplayMode}
shouldShowRegStatus={shouldShowRegStatus}
setShouldShowRegStatus={setShouldShowRegStatus}
shouldShowAdminDetails={shouldShowAdminDetails}
canViewAdminDetails={canViewAdminDetails}
/>

<Container fluid>
<Transition visible={showFilters} animation="slide down">
<Segment raised>
<Button
floated="right"
icon
labelPosition="left"
size="tiny"
secondary
onClick={() => dispatchFilter({ type: 'reset' })}
>
<Icon name="repeat" />
{I18n.t('competitions.index.reset_filters')}
</Button>
<CompetitionsFilters
filterState={filterState}
dispatchFilter={dispatchFilter}
shouldShowAdminDetails={shouldShowAdminDetails}
canViewAdminDetails={canViewAdminDetails}
/>
</Segment>
</Transition>

<Form>
<Form.Group inline>
<CompDisplayCheckboxes
shouldIncludeCancelled={filterState.shouldIncludeCancelled}
dispatchFilter={dispatchFilter}
shouldShowRegStatus={shouldShowRegStatus}
setShouldShowRegStatus={setShouldShowRegStatus}
shouldShowAdminDetails={shouldShowAdminDetails}
canViewAdminDetails={canViewAdminDetails}
displayMode={displayMode}
/>
</Form.Group>
<Form.Field>
<ToggleListOrMapDisplay
displayMode={displayMode}
setDisplayMode={setDisplayMode}
/>
</Form.Field>
</Form>

<Segment basic>
{
displayMode === 'list'
&& (
Expand Down Expand Up @@ -156,7 +210,7 @@ function CompetitionsView({ canViewAdminDetails = false }) {
/>
)
}
</Container>
</Segment>
</Container>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,9 @@ function ConditionalYearHeader({
) {
return (
<Table.Row>
<Table.Cell textAlign="center" colSpan={colSpan}>{startYear(competitions[index])}</Table.Cell>
<Table.Cell textAlign="center" colSpan={colSpan} active>
<Header>{startYear(competitions[index])}</Header>
</Table.Cell>
</Table.Row>
);
}
Expand Down
2 changes: 2 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1528,6 +1528,8 @@ en:
danger: "Danger"
from_date: "From"
to_date: "To"
show_filters: "Show Filters"
hide_filters: "Hide Filters"
reset_filters: "Reset Filters"
#context: selecting a specific year ('year' is a number)
past_from: "Past from %{year}"
Expand Down

0 comments on commit ec318a2

Please sign in to comment.