diff --git a/.env b/.env index ee49f15c99..4a3149d38b 100644 --- a/.env +++ b/.env @@ -20,6 +20,9 @@ VITE_COMMUNITY_PROGRAM_URL=https://community.preciousplastic.com/academy/guides/ VITE_PROFILE_GUIDELINES_URL=https://community.preciousplastic.com/academy/guides/platform VITE_QUESTIONS_GUIDELINES_URL=https://community.preciousplastic.com/academy/guides/guidelines-questions +# Optional variable for limiting the display of member map pins by default on load +VITE_HIDE_MEMBER_PINS_BY_DEFAULT=false + # For testing, VITE_PLATFORM_PROFILES in localStorage is prioritised over this value # All valid options for VITE_PLATFORM_PROFILES: "member,workspace,community-builder,space,collection-point,machine-builder" VITE_PLATFORM_PROFILES="member,workspace,community-builder,collection-point,machine-builder" diff --git a/packages/components/src/CardList/CardList.stories.tsx b/packages/components/src/CardList/CardList.stories.tsx index cbd6ce502d..0e3447cd76 100644 --- a/packages/components/src/CardList/CardList.stories.tsx +++ b/packages/components/src/CardList/CardList.stories.tsx @@ -8,7 +8,7 @@ export default { component: CardList, } as Meta -const allItems = [ +const list = [ { _deleted: false, _id: 'first-one', @@ -43,44 +43,24 @@ const allItems = [ }, ] -const onBlur = () => undefined const onPinClick = () => undefined export const Default: StoryFn = () => { return ( ) } -export const FiltedDisplay: StoryFn = () => { - const filteredList = [allItems[0], allItems[2]] - - return ( - - ) -} - -export const WhenFiltedDisplayIsZero: StoryFn = () => { +export const WhenDisplayIsZero: StoryFn = () => { return ( diff --git a/packages/components/src/CardList/CardList.test.tsx b/packages/components/src/CardList/CardList.test.tsx index 1bd6b0e46f..03f1974c64 100644 --- a/packages/components/src/CardList/CardList.test.tsx +++ b/packages/components/src/CardList/CardList.test.tsx @@ -4,11 +4,7 @@ import { describe, expect, it } from 'vitest' import { render } from '../test/utils' import { EMPTY_LIST, type IProps } from './CardList' -import { - Default, - FiltedDisplay, - WhenFiltedDisplayIsZero, -} from './CardList.stories' +import { Default, WhenDisplayIsZero } from './CardList.stories' describe('CardList', () => { it('Shows all items when no filtering is done', () => { @@ -17,17 +13,9 @@ describe('CardList', () => { expect(getAllByTestId('CardListItem').length).toBe(4) }) - it('Shows only filted items when provided', () => { - const { getAllByTestId } = render( - , - ) - - expect(getAllByTestId('CardListItem').length).toBe(2) - }) - - it('Shows the no items label when filted items is empty', () => { + it('Shows the no item label when filted items is empty', () => { const { getByText } = render( - , + , ) expect(getByText(EMPTY_LIST)).toBeInTheDocument() diff --git a/packages/components/src/CardList/CardList.tsx b/packages/components/src/CardList/CardList.tsx index 77f2780653..b3d42f1d97 100644 --- a/packages/components/src/CardList/CardList.tsx +++ b/packages/components/src/CardList/CardList.tsx @@ -1,60 +1,68 @@ +import { useEffect, useState } from 'react' import Masonry, { ResponsiveMasonry } from 'react-responsive-masonry' import { Flex, Text } from 'theme-ui' +import { Button } from '../Button/Button' import { CardListItem } from '../CardListItem/CardListItem' import { Icon } from '../Icon/Icon' -import { Loader } from '../Loader/Loader' import type { IMapPin } from 'oa-shared' export interface IProps { columnsCountBreakPoints?: { [key: number]: number } - filteredList: IMapPin[] | null list: IMapPin[] - onBlur: () => void onPinClick: (arg: IMapPin) => void selectedPin: IMapPin | undefined viewport: string } +const DEFAULT_BREAKPOINTS = { 600: 1, 1100: 2, 1600: 3 } export const EMPTY_LIST = 'Oh nos! Nothing to show!' +const ITEMS_PER_RENDER = 20 export const CardList = (props: IProps) => { - const { - columnsCountBreakPoints, - filteredList, - list, - onBlur, - onPinClick, - selectedPin, - viewport, - } = props + const [renderCount, setRenderCount] = useState(ITEMS_PER_RENDER) + const [displayItems, setDisplayItems] = useState([]) + const { list, onPinClick, selectedPin, viewport } = props - const listToShow = filteredList === null ? list : filteredList - const displayItems = listToShow - .sort( - (a, b) => - Date.parse(b.creator?._lastActive || '0') - - Date.parse(a.creator?._lastActive || '0'), - ) - .map((item) => { - const isSelectedPin = item._id === selectedPin?._id - return ( - + useEffect(() => { + setRenderCount(ITEMS_PER_RENDER) + }, [list]) + + useEffect(() => { + const toRender = list + .sort( + (a, b) => + Date.parse(b.creator?._lastActive || '0') - + Date.parse(a.creator?._lastActive || '0'), ) - }) + .slice(0, renderCount) + .map((item) => { + const isSelectedPin = item._id === selectedPin?._id + + return ( + + ) + }) + + setDisplayItems(toRender) + }, [renderCount, list]) + + const addRenderItems = () => + setRenderCount((count) => count + ITEMS_PER_RENDER) + + const hasMore = !(displayItems.length === list.length) - const isListEmpty = displayItems.length === 0 - const hasListLoaded = list - const results = `${displayItems.length} result${ - displayItems.length == 1 ? '' : 's' - } in view` + const isListEmpty = list.length === 0 + const results = `${list.length} result${list.length == 1 ? '' : 's'} in view` + const columnsCountBreakPoints = + props.columnsCountBreakPoints || DEFAULT_BREAKPOINTS return ( { padding: 2, }} > - {!hasListLoaded && } - {hasListLoaded && ( + + {results} + + Most recently active + + + + {isListEmpty && EMPTY_LIST} + {!isListEmpty && ( <> - - {results} - - Most recently active - + + {displayItems} + + {hasMore && ( + + - - {isListEmpty && EMPTY_LIST} - {!isListEmpty && ( - - {displayItems} - )} )} diff --git a/packages/components/src/CardProfile/CardDetailsMemberProfile.tsx b/packages/components/src/CardProfile/CardDetailsMemberProfile.tsx index ae0f7897a5..2e6548c228 100644 --- a/packages/components/src/CardProfile/CardDetailsMemberProfile.tsx +++ b/packages/components/src/CardProfile/CardDetailsMemberProfile.tsx @@ -1,5 +1,6 @@ import { Avatar, Box, Flex } from 'theme-ui' +import defaultProfileImage from '../../assets/images/default_member.svg' import { MemberBadge } from '../MemberBadge/MemberBadge' import { ProfileTagsList } from '../ProfileTagsList/ProfileTagsList' import { Username } from '../Username/Username' @@ -24,29 +25,26 @@ export const CardDetailsMemberProfile = ({ creator, isLink }: IProps) => { alignContent: 'stretch', }} > - {userImage && ( - - - - - - - )} - {!userImage && } + + + + + + { const { creator } = item - const isMember = creator?.profileType === 'member' + const isWorkspace = creator?.profileType && creator?.profileType !== 'member' + const isMember = !isWorkspace && creator return ( + {isWorkspace && ( + + )} {isMember && ( )} - {!isMember && creator && ( - + {!isWorkspace && !isMember && ( + )} - {!creator && } ) } diff --git a/packages/components/src/Loader/Loader.tsx b/packages/components/src/Loader/Loader.tsx index 92f5fcaf05..57668e7529 100644 --- a/packages/components/src/Loader/Loader.tsx +++ b/packages/components/src/Loader/Loader.tsx @@ -43,7 +43,7 @@ export const Loader = ({ label, sx }: Props) => { /> )} - {label || 'loading...'} + {label || 'Loading...'} diff --git a/packages/components/src/MapFilterList/MapFilterList.tsx b/packages/components/src/MapFilterList/MapFilterList.tsx index 8a318d850d..b490169fc0 100644 --- a/packages/components/src/MapFilterList/MapFilterList.tsx +++ b/packages/components/src/MapFilterList/MapFilterList.tsx @@ -32,14 +32,15 @@ export const MapFilterList = (props: IProps) => { ({ filterType }) => filterType === 'profileType', ) - const tagFilters = availableFilters.filter( - ({ filterType }) => filterType === 'profileTag', - ) + const tagFilters = availableFilters + .slice(0) + .filter(({ filterType }) => filterType === 'profileTag') + .sort((a, b) => (a.label > b.label ? 1 : 0)) const isActive = (checkingFilter: string) => !!activeFilters.find((filter) => filter.label === checkingFilter) - const buttonLabel = `Show ${pinCount} result${pinCount === 1 ? '' : 's'}` + const buttonLabel = `${pinCount} result${pinCount === 1 ? '' : 's'} in current view` return ( { padding: 2, }} > - - So what are you looking for? - + + + Filter what you see + + + + Zoom out on the map to search the whole world + + { {tagFilters.length > 0 && ( - Activities + Spaces activities {tagFilters.map((typeFilter, index) => { const onClick = () => onFilterChange(typeFilter) diff --git a/packages/components/src/MapMemberCard/MapMemberCard.stories.tsx b/packages/components/src/MapMemberCard/MapMemberCard.stories.tsx deleted file mode 100644 index da0140b779..0000000000 --- a/packages/components/src/MapMemberCard/MapMemberCard.stories.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { faker } from '@faker-js/faker' - -import { MapMemberCard } from './MapMemberCard' - -import type { Meta } from '@storybook/react' - -export default { - title: 'Map/MapMemberCard', - component: MapMemberCard, -} as Meta - -export const Default = { - args: { - imageUrl: 'https://placekitten.com/450/450', - description: `${faker.lorem.sentence()}`, - user: { - countryCode: faker.address.countryCode('alpha-2'), - userName: faker.internet.userName(), - isSupporter: faker.datatype.boolean(), - isVerified: faker.datatype.boolean(), - }, - heading: `${faker.lorem.word()}`, - isEditable: false, - comments: null, - }, -} - -export const LoadingState = { - args: { - loading: true, - imageUrl: 'https://placekitten.com/450/450', - description: `${faker.lorem.sentence()}`, - user: { - countryCode: faker.address.countryCode('alpha-2'), - userName: faker.internet.userName(), - isSupporter: faker.datatype.boolean(), - isVerified: faker.datatype.boolean(), - }, - heading: `${faker.lorem.word()}`, - isEditable: false, - }, -} - -export const ModerationComments = { - args: { - imageUrl: 'https://placekitten.com/450/450', - description: `${faker.lorem.sentence()}`, - comments: `${faker.lorem.sentence()}`, - user: { - countryCode: faker.address.countryCode('alpha-2'), - userName: faker.internet.userName(), - isSupporter: faker.datatype.boolean(), - isVerified: faker.datatype.boolean(), - }, - heading: `${faker.lorem.word()}`, - isEditable: false, - }, -} diff --git a/packages/components/src/MapMemberCard/MapMemberCard.test.tsx b/packages/components/src/MapMemberCard/MapMemberCard.test.tsx deleted file mode 100644 index 5130870eab..0000000000 --- a/packages/components/src/MapMemberCard/MapMemberCard.test.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import '@testing-library/jest-dom/vitest' - -import { describe, expect, it } from 'vitest' - -import { render } from '../test/utils' -import { MapMemberCard } from './MapMemberCard' -import { Default, ModerationComments } from './MapMemberCard.stories' - -describe('MapMemberCard', () => { - it('includes description if it exists', () => { - const { getByText, getByTestId } = render( - , - ) - - expect(getByText(Default.args.description)).toBeInTheDocument() - expect(() => getByTestId('MapMemberCard: moderation comments')).toThrow() - }) - - it('shows moderation comments if they exist', () => { - const { getByText, getByTestId } = render( - , - ) - expect( - getByTestId('MapMemberCard: moderation comments'), - ).toBeInTheDocument() - expect(getByText(ModerationComments.args.comments)).toBeInTheDocument() - }) -}) diff --git a/packages/components/src/MapMemberCard/MapMemberCard.tsx b/packages/components/src/MapMemberCard/MapMemberCard.tsx deleted file mode 100644 index 3e22090319..0000000000 --- a/packages/components/src/MapMemberCard/MapMemberCard.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import { keyframes } from '@emotion/react' -import { Alert, AspectRatio, Box, Card, Image, Text } from 'theme-ui' - -import { Username } from '../Username/Username' - -import type { User } from '../types/common' - -export interface Props { - loading?: boolean - imageUrl: string - description: string - comments: string | null - user: User - heading: string -} - -const wave = keyframes` - from { - background: lightgrey; - } - to { - background: grey; - } -` - -export const MapMemberCard = (props: Props) => { - const { imageUrl, description, user, heading, comments } = props - - return ( - - - {!!props.loading && ( - <> - - - - )} - {!props.loading && ( - <> - - - - - - {heading} - -
- -
- - {description} - - - {comments ? ( - - - This map pin has been marked as requiring further changes. - {comments} - - - ) : null} -
- - )} -
-
- ) -} diff --git a/packages/components/src/PinProfile/PinProfile.tsx b/packages/components/src/PinProfile/PinProfile.tsx index 7defc71ba7..3d74490a79 100644 --- a/packages/components/src/PinProfile/PinProfile.tsx +++ b/packages/components/src/PinProfile/PinProfile.tsx @@ -17,7 +17,7 @@ export const PinProfile = (props: IProps) => { const { item, onClose } = props const { creator } = item - const isMember = creator?.profileType === 'member' + const isWorkspace = creator?.profileType && creator?.profileType !== 'member' return ( @@ -34,11 +34,12 @@ export const PinProfile = (props: IProps) => { - {!isMember && creator?.isContactableByPublic !== false && ( + {isWorkspace && creator?.isContactableByPublic !== false && ( - - -
- - - - - onChange(selected)} - onClose={toggleFilterMobileModal} - /> - -
- ) -} diff --git a/src/pages/Maps/Content/Controls/GroupingFilterDesktop.tsx b/src/pages/Maps/Content/Controls/GroupingFilterDesktop.tsx deleted file mode 100644 index e91867c3dd..0000000000 --- a/src/pages/Maps/Content/Controls/GroupingFilterDesktop.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import React from 'react' -import { Select } from 'oa-components' -import { Box } from 'theme-ui' - -import type { FilterGroup } from './transformAvailableFiltersToGroups' - -interface IProps { - availableFilters: FilterGroup[] - onChange: (selectedItems: string[]) => void -} - -export const GroupingFilterDesktop = (props: IProps) => { - const onSelectChange = (selectedOptions) => { - const arr = selectedOptions.map((option) => option.value) - props.onChange(arr) - } - - return ( - -