Verified Commit 2cfc6239 authored by Marek Veselý's avatar Marek Veselý
Browse files

refactor: deduplicate team selector overflow list

parent 8dfb6cea
Loading
Loading
Loading
Loading
+9 −54
Original line number Diff line number Diff line
import {
  Boundary,
  ButtonGroup,
  Classes,
  OverflowList,
  Popover,
} from '@blueprintjs/core'
import { css } from '@emotion/css'
import { useSubscribedTeams } from '@inject/frontend'
import type { Team } from '@inject/graphql'
import { useContext, useState } from 'react'
import { useContext } from 'react'
import { ExerciseContext } from '../ExerciseContext'
import { TeamSelector } from '../TeamSelector'
import { useTeamSelectorProps } from '../TeamSelector/useTeamSelectorProps'
import MilestoneCards from './MilestoneCards'
import Title from './Title'

// TODO: convert to table

@@ -23,42 +16,10 @@ export const Milestones = () => {
    context: 'analyst',
  })

  const [selectedId, setSelectedId] = useState<string | undefined>()

  const handleClick = (item: Team | undefined) => () => {
    setSelectedId(item?.id)
  }

  const overflowRenderer = (overflowItems: (Team | undefined)[]) => (
    <div style={{ marginLeft: 'auto' }}>
      <Popover
        content={
          <ButtonGroup vertical style={{ padding: 8 }}>
            {overflowItems.map(item => (
              <Title
                key={item?.id || 'AllTeamsTab'}
                team={item}
                onClick={handleClick(item)}
                active={selectedId === item?.id}
              />
            ))}
          </ButtonGroup>
        }
        position='bottom-left'
      >
        <span className={Classes.BREADCRUMBS_COLLAPSED} style={{ margin: 4 }} />
      </Popover>
    </div>
  )

  const visibleItemRenderer = (item: Team | undefined) => (
    <Title
      key={item?.id || 'AllTeamsTab'}
      team={item}
      onClick={handleClick(item)}
      active={selectedId === item?.id}
    />
  )
  const teamSelectorProps = useTeamSelectorProps({
    teams,
    includeAll: true,
  })

  return (
    <div
@@ -70,15 +31,9 @@ export const Milestones = () => {
        height: 100%;
      `}
    >
      <OverflowList
        style={{ alignItems: 'center' }}
        collapseFrom={Boundary.END}
        items={[undefined, ...teams]}
        overflowRenderer={overflowRenderer}
        visibleItemRenderer={visibleItemRenderer}
      />
      <TeamSelector {...teamSelectorProps} />

      <MilestoneCards teamId={selectedId} />
      <MilestoneCards teamId={teamSelectorProps.selectedTeam?.id} />
    </div>
  )
}
+0 −94
Original line number Diff line number Diff line
import {
  Boundary,
  ButtonGroup,
  Classes,
  Divider,
  OverflowList,
  Popover,
} from '@blueprintjs/core'
import { css } from '@emotion/css'
import { type Team } from '@inject/graphql'
import { TabButton } from '@inject/shared'
import type { FC, ReactNode } from 'react'
import { useEffect, useState } from 'react'

const wrapper = css`
  display: flex;
  flex-direction: column;
  height: 100%;
  overflow: auto;
`

interface TeamOrAllSelectPageProps {
  teams: Team[]
  getChildren: (selectedTeam: Team | undefined) => ReactNode
  allTeamsLabel?: string
  initialSelectedTeam?: Team | undefined
}

export const TeamOrAllSelectPage: FC<TeamOrAllSelectPageProps> = ({
  teams,
  getChildren,
  allTeamsLabel = 'All teams',
  initialSelectedTeam,
}) => {
  const [selectedTeam, setSelectedTeam] = useState<Team | undefined>(
    initialSelectedTeam
  )
  const handleClick = (item: Team | undefined) => () => setSelectedTeam(item)

  useEffect(() => {
    if (selectedTeam && !teams.find(team => team.id === selectedTeam.id)) {
      setSelectedTeam(undefined)
    }
  }, [teams, selectedTeam])

  const overflowRenderer = (overflowItems: (Team | undefined)[]) => (
    <div style={{ marginLeft: 'auto' }}>
      <Popover
        content={
          <ButtonGroup vertical style={{ padding: 8 }}>
            {overflowItems.map(item => (
              <TabButton
                key={item?.id || 'AllTeamsTab'}
                active={
                  selectedTeam?.id === item?.id || (!item && !selectedTeam)
                }
                onClick={handleClick(item)}
              >
                {item?.name || allTeamsLabel}
              </TabButton>
            ))}
          </ButtonGroup>
        }
        position='bottom-left'
      >
        <span className={Classes.BREADCRUMBS_COLLAPSED} style={{ margin: 4 }} />
      </Popover>
    </div>
  )

  const visibleItemRenderer = (item: Team | undefined) => (
    <TabButton
      key={item?.id || 'AllTeamsTab'}
      active={selectedTeam?.id === item?.id || (!item && !selectedTeam)}
      onClick={handleClick(item)}
    >
      {item?.name || allTeamsLabel}
    </TabButton>
  )

  return (
    <div className={wrapper}>
      <OverflowList
        style={{ alignItems: 'center', marginBottom: 8 }}
        collapseFrom={Boundary.END}
        items={[undefined, ...teams]}
        overflowRenderer={overflowRenderer}
        visibleItemRenderer={visibleItemRenderer}
      />
      <Divider />
      {getChildren(selectedTeam)}
    </div>
  )
}
+6 −64
Original line number Diff line number Diff line
import {
  Boundary,
  ButtonGroup,
  Classes,
  Divider,
  OverflowList,
  Popover,
} from '@blueprintjs/core'
import { Divider } from '@blueprintjs/core'
import { css } from '@emotion/css'
import type { Team } from '@inject/graphql'
import { TabButton } from '@inject/shared'
import type { FC, ReactNode } from 'react'
import { useEffect, useState } from 'react'
import { TeamSelector } from '../TeamSelector'
import { useTeamSelectorProps } from '../TeamSelector/useTeamSelectorProps'

const wrapper = css`
  display: flex;
@@ -19,13 +12,6 @@ const wrapper = css`
  overflow: auto;
`

const buttonGroupContainer = css`
  padding: 0.5rem;
  min-width: max-content;
  max-height: 32rem;
  overflow-y: auto;
`

interface TeamSelectPageProps {
  teams: Team[]
  getChildren: (selectedTeam: Team) => ReactNode
@@ -35,57 +21,13 @@ export const TeamSelectPage: FC<TeamSelectPageProps> = ({
  teams,
  getChildren,
}) => {
  const [selectedTeam, setSelectedTeam] = useState<Team>(teams[0])
  const handleClick = (item: Team) => () => setSelectedTeam(item)

  useEffect(() => {
    setSelectedTeam(prev => teams.find(team => team.id === prev.id) || teams[0])
  }, [teams])

  const overflowRenderer = (overflowItems: Team[]) => (
    <div style={{ marginLeft: 'auto' }}>
      <Popover
        content={
          <ButtonGroup vertical className={buttonGroupContainer}>
            {overflowItems.map(item => (
              <TabButton
                key={item.id}
                active={selectedTeam.id === item.id}
                onClick={handleClick(item)}
              >
                {item.name}
              </TabButton>
            ))}
          </ButtonGroup>
        }
        position='bottom-left'
      >
        <span className={Classes.BREADCRUMBS_COLLAPSED} style={{ margin: 4 }} />
      </Popover>
    </div>
  )

  const visibleItemRenderer = (item: Team) => (
    <TabButton
      key={item.id}
      active={selectedTeam.id === item.id}
      onClick={handleClick(item)}
    >
      {item.name}
    </TabButton>
  )
  const teamSelectorProps = useTeamSelectorProps({ teams })

  return (
    <div className={wrapper}>
      <OverflowList
        style={{ alignItems: 'center' }}
        collapseFrom={Boundary.END}
        items={teams}
        overflowRenderer={overflowRenderer}
        visibleItemRenderer={visibleItemRenderer}
      />
      <TeamSelector {...teamSelectorProps} />
      <Divider />
      {getChildren(selectedTeam)}
      {getChildren(teamSelectorProps.selectedTeam)}
    </div>
  )
}
+22 −0
Original line number Diff line number Diff line
import type { Team } from '@inject/graphql'
import { TabButton } from '@inject/shared'
import type { FC } from 'react'

interface TeamButtonProps {
  team: Team | undefined
  selectedTeamId: string | undefined
  onClick: () => void
}

export const TeamButton: FC<TeamButtonProps> = ({
  team,
  selectedTeamId,
  onClick,
}) => (
  <TabButton
    active={selectedTeamId === team?.id || (!team && !selectedTeamId)}
    onClick={onClick}
  >
    {team?.name || 'All teams'}
  </TabButton>
)
+84 −0
Original line number Diff line number Diff line
import {
  Boundary,
  ButtonGroup,
  Classes,
  OverflowList,
  Popover,
} from '@blueprintjs/core'
import { css, cx } from '@emotion/css'
import type { Team } from '@inject/graphql'
import type { Dispatch, FC, SetStateAction } from 'react'
import { TeamButton } from './TeamButton'
import type { TeamSelectorProps } from './types'

export const TeamSelector: FC<TeamSelectorProps> = ({
  selectedTeam,
  setSelectedTeam,
  items,
}) => {
  const handleClick = (item: Team | undefined) =>
    (setSelectedTeam as Dispatch<SetStateAction<Team | undefined>>)(item)

  const overflowRenderer = (overflowItems: (Team | undefined)[]) => (
    <div
      className={css`
        margin-left: auto;
      `}
    >
      <Popover
        content={
          <ButtonGroup
            vertical
            className={css`
              display: flex;
              flex-direction: column;
              padding: 0.5rem;
              overflow-y: auto;
              max-height: 60vh;
            `}
          >
            {overflowItems.map(item => (
              <TeamButton
                key={item?.id || 'all-teams-tab'}
                team={item}
                selectedTeamId={selectedTeam?.id}
                onClick={() => handleClick(item)}
              />
            ))}
          </ButtonGroup>
        }
        position='bottom-left'
      >
        <span
          className={cx(
            Classes.BREADCRUMBS_COLLAPSED,
            css`
              margin: 0.25rem;
            `
          )}
        />
      </Popover>
    </div>
  )

  const visibleItemRenderer = (item: Team | undefined) => (
    <TeamButton
      key={item?.id || 'all-teams-tab'}
      team={item}
      selectedTeamId={selectedTeam?.id}
      onClick={() => handleClick(item)}
    />
  )

  return (
    <OverflowList
      className={css`
        align-items: center;
      `}
      collapseFrom={Boundary.END}
      items={items}
      overflowRenderer={overflowRenderer}
      visibleItemRenderer={visibleItemRenderer}
    />
  )
}
Loading