@imerss/inat-components 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +24 -13
  2. package/dist/index.d.ts +87 -75
  3. package/dist/index.js +261 -173
  4. package/dist/standalone.min.js +1 -1
  5. package/package.json +10 -12
  6. package/PUBLISH.md +0 -13
  7. package/declaration.d.ts +0 -2
  8. package/public/favicon.ico +0 -0
  9. package/public/index.html +0 -20
  10. package/public/logo192.png +0 -0
  11. package/public/logo512.png +0 -0
  12. package/public/manifest.json +0 -25
  13. package/public/robots.txt +0 -3
  14. package/public/standalone.html +0 -55
  15. package/rollup.config.js +0 -47
  16. package/src/__shared/api/common-taxa.ts +0 -57
  17. package/src/__shared/api/favourites.ts +0 -57
  18. package/src/__shared/api/index.ts +0 -4
  19. package/src/__shared/api/recent-observations.ts +0 -44
  20. package/src/__shared/api/summary.ts +0 -86
  21. package/src/__shared/constants.ts +0 -6
  22. package/src/__shared/index.ts +0 -4
  23. package/src/__shared/typings.ts +0 -83
  24. package/src/__shared/utils/configUtils.ts +0 -25
  25. package/src/__shared/utils/dateUtils.ts +0 -14
  26. package/src/__shared/utils/index.ts +0 -3
  27. package/src/__shared/utils/numberUtils.ts +0 -1
  28. package/src/components/common-taxa/__tests__/common-taxa.test.tsx +0 -14
  29. package/src/components/common-taxa/common-taxa.tsx +0 -73
  30. package/src/components/error/__tests__/error.test.tsx +0 -9
  31. package/src/components/error/error.module.scss +0 -7
  32. package/src/components/error/error.tsx +0 -15
  33. package/src/components/favourites/__tests__/favourites.test.tsx +0 -13
  34. package/src/components/favourites/favourites.tsx +0 -73
  35. package/src/components/hooks/hooks.tsx +0 -34
  36. package/src/components/loader/__tests__/loader.test.tsx +0 -10
  37. package/src/components/loader/loader.tsx +0 -26
  38. package/src/components/no-results/__tests__/no-results.test.tsx +0 -9
  39. package/src/components/no-results/no-results.tsx +0 -9
  40. package/src/components/observation/__tests__/observation.test.tsx +0 -14
  41. package/src/components/observation/observation.module.scss +0 -20
  42. package/src/components/observation/observation.tsx +0 -30
  43. package/src/components/recent-observations/__tests__/recent-observations.test.tsx +0 -9
  44. package/src/components/recent-observations/recent-observations.tsx +0 -84
  45. package/src/components/seasonality-graph/__tests__/seasonality-graph.test.tsx +0 -9
  46. package/src/components/seasonality-graph/seasonality-graph.module.scss +0 -7
  47. package/src/components/seasonality-graph/seasonality-graph.tsx +0 -46
  48. package/src/components/shared/css/general.module.scss +0 -73
  49. package/src/components/summary/ObserverList.tsx +0 -24
  50. package/src/components/summary/__tests__/summary.test.tsx +0 -18
  51. package/src/components/summary/summary.module.scss +0 -88
  52. package/src/components/summary/summary.tsx +0 -91
  53. package/src/components/tabs/__tests__/tabs.test.tsx +0 -16
  54. package/src/components/tabs/tabs.module.scss +0 -28
  55. package/src/components/tabs/tabs.tsx +0 -65
  56. package/src/components/taxon-panel/__tests__/taxon-panel.test.tsx +0 -10
  57. package/src/components/taxon-panel/taxon-panel.module.scss +0 -7
  58. package/src/components/taxon-panel/taxon-panel.tsx +0 -110
  59. package/src/components/year-dropdown/__tests__/year-dropdown.test.tsx +0 -14
  60. package/src/components/year-dropdown/year-dropdown.module.scss +0 -3
  61. package/src/components/year-dropdown/year-dropdown.tsx +0 -37
  62. package/src/hooks/useLoadSourceData.ts +0 -81
  63. package/src/index.ts +0 -15
  64. package/src/types/index.d.ts +0 -17
  65. package/src/types/standalone.d.ts +0 -6
  66. package/tsconfig.json +0 -6
@@ -1,88 +0,0 @@
1
- .summaryPage {
2
- position: relative;
3
-
4
- section {
5
- display: flex;
6
- }
7
- }
8
-
9
- .top {
10
- list-style-type: none;
11
- margin: 0;
12
- padding: 0;
13
-
14
- .avatar, .noAvatar {
15
- margin-right: 10px;
16
- border-radius: 20px;
17
- width: 40px;
18
- height: 40px;
19
- }
20
-
21
- .noAvatar {
22
- display: inline-block;
23
- background-color: #efefef;
24
- }
25
-
26
- h3 {
27
- flex: 1;
28
- color: black;
29
- }
30
-
31
- label {
32
- flex: 0;
33
- background-color: #efefef;
34
- color: #666666;
35
- padding: 2px 8px;
36
- margin-top: 6px;
37
- border-radius: 3px;
38
- font-weight: bold;
39
- font-size: 16px;
40
- }
41
-
42
- li {
43
- border-radius: 3px;
44
-
45
- a {
46
- display: flex;
47
- align-items: center;
48
- text-decoration: none;
49
- }
50
-
51
- &:hover {
52
- background-color: #efefef;
53
- }
54
- }
55
- }
56
-
57
- .countSummary {
58
- list-style-type: none;
59
- line-height: 28px;
60
- font-size: 16px;
61
- padding: 0;
62
- margin: 0 0 30px;
63
- }
64
-
65
- .seasonalityBlock {
66
- flex: 3;
67
- }
68
-
69
- .observersBlock {
70
- flex: 1;
71
- margin-left: 40px;
72
- margin-bottom: -40px;
73
- }
74
-
75
- @media screen and (max-width: 800px) {
76
- .summaryPage section {
77
- display: block;
78
- }
79
-
80
- .seasonalityBlock {
81
- height: 250px;
82
- margin-bottom: 80px;
83
- }
84
-
85
- .observersBlock {
86
- margin-left: 0;
87
- }
88
- }
@@ -1,91 +0,0 @@
1
- import React from "react";
2
- import { numberWithCommas, DataSource, getSummary, GeneralClasses } from "../../__shared";
3
- import { ObserverList } from './ObserverList';
4
- import Loader from "../loader/loader";
5
- import { SeasonalityGraph } from "../seasonality-graph/seasonality-graph";
6
- import styles from "./summary.module.scss";
7
- import { useLoadSourceData } from "../../hooks/useLoadSourceData";
8
-
9
- export type SummaryProps = {
10
- taxonId: number;
11
- placeId: number;
12
- year: number;
13
- source: DataSource;
14
- dataUrl?: string;
15
- generalClasses?: GeneralClasses;
16
- className?: string;
17
- tabDesc?: string;
18
- observersListClass?: string;
19
- statsCountSummaryClass?: string;
20
- };
21
-
22
- export const Summary = ({
23
- source,
24
- dataUrl,
25
- taxonId,
26
- placeId,
27
- year,
28
- generalClasses,
29
- className,
30
- tabDesc,
31
- observersListClass,
32
- statsCountSummaryClass
33
- }: SummaryProps) => {
34
- const { loading, results: summaryData } = useLoadSourceData({
35
- year,
36
- taxonId,
37
- placeId,
38
- dataUrl,
39
- source,
40
- isSummaryData: true,
41
-
42
- // shim to standardize the response from the `action` method with a top-level `results` property. This
43
- // just lets the useLoadSourceData hook work for all tab components
44
- action: async (data) => {
45
- const results = await getSummary(data);
46
-
47
- return { results };
48
- }
49
- });
50
-
51
- let countSummaryClasses = styles.countSummary;
52
- if (statsCountSummaryClass) {
53
- countSummaryClasses += ` ${statsCountSummaryClass}`;
54
- }
55
- let descClasses = styles.tabDesc;
56
- if (generalClasses?.tabDesc) {
57
- descClasses += ` ${generalClasses.tabDesc}`;
58
- }
59
-
60
- let classList = styles.summaryPage;
61
- if (className) {
62
- classList += ` ${className}`;
63
- }
64
-
65
- return (
66
- <div className={classList}>
67
- {tabDesc && <p className={descClasses}>{tabDesc}</p>}
68
- <Loader loading={loading} />
69
- <ul className={countSummaryClasses}>
70
- <li>Total number of observers: <b>{summaryData.observers?.totalCount ? numberWithCommas(summaryData.observers.totalCount): ""}</b></li>
71
- <li>Total number of observations: <b>{summaryData.observations?.totalCount ? numberWithCommas(summaryData.observations.totalCount): ""}</b></li>
72
- </ul>
73
- <section>
74
- <div className={styles.seasonalityBlock}>
75
- <h1>Seasonality</h1>
76
- {summaryData.seasonalityData && <SeasonalityGraph data={summaryData.seasonalityData.monthOfYear} />}
77
- </div>
78
- <div className={styles.observersBlock}>
79
- <h1>Top observers</h1>
80
- {summaryData.observers?.top && (
81
- <ObserverList
82
- observers={summaryData.observers.top}
83
- className={observersListClass}
84
- generalClasses={generalClasses}
85
- />
86
- )}
87
- </div>
88
- </section>
89
- </div>
90
- );
91
- }
@@ -1,16 +0,0 @@
1
- import {render} from '@testing-library/react';
2
- import Tabs from '../tabs';
3
- import {Tab} from "../../../index";
4
-
5
- const defaultProps = {
6
- selectedTab: Tab.recent,
7
- onChangeTab: () => null,
8
- features: {}
9
- };
10
-
11
- describe('Tabs', () => {
12
- it('should render successfully', () => {
13
- const {baseElement} = render(<Tabs {...defaultProps} />);
14
- expect(baseElement).toBeTruthy();
15
- });
16
- });
@@ -1,28 +0,0 @@
1
- .tabs {
2
- display: flex;
3
- flex-direction: row;
4
- list-style: none;
5
- font-size: 18px;
6
- padding: 0;
7
- margin-bottom: 30px;
8
-
9
- li {
10
- margin: 0 20px;
11
- padding: 4px 20px;
12
- cursor: pointer;
13
- color: #999999;
14
- transition: all 0.2s ease-in-out;
15
-
16
- &.selected, &:hover {
17
- border-bottom: 5px solid #0099cc;
18
- color: black;
19
- }
20
- }
21
- }
22
-
23
- @media screen and (max-width: 600px) {
24
- .tabs li {
25
- margin: 0 5px;
26
- padding: 4px 6px;
27
- }
28
- }
@@ -1,65 +0,0 @@
1
- import React from "react";
2
- import { Feature, TaxonPanelFeatures } from "../../__shared";
3
- import styles from "./tabs.module.scss";
4
- import { useFeatureTitles } from "../hooks/hooks";
5
-
6
- export type TabsProps = {
7
- selectedTab: Feature;
8
- onChangeTab: (tab: Feature) => void;
9
- features: TaxonPanelFeatures;
10
- className?: string;
11
- }
12
-
13
- const Tabs = ({ selectedTab, onChangeTab, features, className }: TabsProps) => {
14
- const titles = useFeatureTitles(features);
15
-
16
- let classes = styles.tabs;
17
- if (className) {
18
- classes += ` ${className}`;
19
- }
20
-
21
- const items = features.map((item) => {
22
- if (item.feature == Feature.recentObservations) {
23
- return (
24
- <li key={Feature.recentObservations}
25
- onClick={() => onChangeTab(Feature.recentObservations)}
26
- className={selectedTab === Feature.recentObservations ? styles.selected : ""}>
27
- {titles[Feature.recentObservations]}
28
- </li>
29
- );
30
- } else if (item.feature == Feature.commonTaxa) {
31
- return (
32
- <li key={Feature.commonTaxa}
33
- onClick={() => onChangeTab(Feature.commonTaxa)}
34
- className={selectedTab === Feature.commonTaxa ? styles.selected : ""}>
35
- {titles[Feature.commonTaxa]}
36
- </li>
37
- );
38
- } else if (item.feature == Feature.favourites) {
39
- return (
40
- <li key={Feature.favourites}
41
- onClick={() => onChangeTab(Feature.favourites)}
42
- className={selectedTab === Feature.favourites ? styles.selected : ""}>
43
- {titles[Feature.favourites]}
44
- </li>
45
- );
46
- } else if (item.feature == Feature.stats) {
47
- return (
48
- <li key={Feature.stats} onClick={() => onChangeTab(Feature.stats)} className={selectedTab === Feature.stats ? styles.selected : ""}>
49
- {titles[Feature.stats]}
50
- </li>
51
- );
52
- }
53
-
54
- // possibly a user has included something wacky
55
- return undefined;
56
- });
57
-
58
- return (
59
- <ul className={classes}>
60
- {items}
61
- </ul>
62
- );
63
- }
64
-
65
- export default Tabs;
@@ -1,10 +0,0 @@
1
- import {render} from '@testing-library/react';
2
-
3
- import TaxonPanel from '../taxon-panel';
4
-
5
- describe('TaxonPanel', () => {
6
- it('should render successfully', () => {
7
- const {baseElement} = render(<TaxonPanel />);
8
- expect(baseElement).toBeTruthy();
9
- });
10
- });
@@ -1,7 +0,0 @@
1
- /*
2
- * Replace this with your own classes
3
- *
4
- * e.g.
5
- * .container {
6
- * }
7
- */
@@ -1,110 +0,0 @@
1
- import React, { useMemo, useState } from "react";
2
- import { C, getSourceFile, DataSource, DataSourceEnum, Feature, GeneralClasses, TaxonPanelFeatures } from "../../__shared";
3
- import { RecentObservations } from '../recent-observations/recent-observations';
4
- import { CommonTaxa } from '../common-taxa/common-taxa';
5
- import { Favourites } from "../favourites/favourites"
6
- import { Summary } from "../summary/summary";
7
- import Years from "../year-dropdown/year-dropdown";
8
- import Tabs from "../tabs/tabs";
9
- import { convertFeaturesToObj, useFeatureTitles } from "../hooks/hooks";
10
- import styles from "../shared/css/general.module.scss";
11
-
12
- export type TaxonPanelProps = {
13
- taxon: {
14
- id: number;
15
- str: string;
16
- };
17
- place: {
18
- id: number;
19
- str: string;
20
- }
21
- dataSourceBaseUrl: string;
22
- dataSource: DataSource;
23
- features: TaxonPanelFeatures;
24
- generalClasses: GeneralClasses;
25
- itemWidth?: number;
26
- }
27
-
28
- const TaxonPanel = ({ taxon, place, features, dataSource, dataSourceBaseUrl, itemWidth, generalClasses }: TaxonPanelProps) => {
29
- const [tab, setTab] = useState(features[0].feature);
30
- const [year, setYear] = useState<string>("all");
31
- const featuresObj = useMemo(() => {
32
- return convertFeaturesToObj(features);
33
- }, [features]);
34
- const titles = useFeatureTitles(featuresObj);
35
-
36
- const getProps = (feature: Feature): any => {
37
- const props: any = {
38
- source: dataSource,
39
- itemWidth,
40
- tabDesc: featuresObj[feature]?.desc,
41
- generalClasses,
42
- className: featuresObj[feature]?.className
43
- };
44
- if (dataSource === DataSourceEnum.url) {
45
- props.dataUrl = dataSourceBaseUrl + '/' + getSourceFile(feature, taxon, place, year);
46
- } else {
47
- props.placeId = place.id;
48
- props.taxonId = taxon.id;
49
- }
50
-
51
- if (feature !== Feature.stats) {
52
- props.numResults = featuresObj[feature]?.numResults;
53
- }
54
-
55
- if (feature !== Feature.recentObservations) {
56
- props.year = year;
57
- }
58
-
59
- return props;
60
- };
61
-
62
- const getCurrentTab = () => {
63
- switch (tab) {
64
- case Feature.recentObservations:
65
- return <RecentObservations {...getProps(Feature.recentObservations)} />;
66
- case Feature.commonTaxa:
67
- return <CommonTaxa {...getProps(Feature.commonTaxa)} />;
68
- case Feature.favourites:
69
- return <Favourites {...getProps(Feature.favourites)} />;
70
-
71
- case Feature.stats:
72
- return (
73
- <Summary
74
- {...getProps(Feature.stats)}
75
- observersListClass={featuresObj[Feature.stats]?.observersListClass}
76
- statsCountSummaryClass={featuresObj[Feature.stats]?.statsCountSummaryClass}
77
- />
78
- );
79
- }
80
- };
81
-
82
- const tabsClass = generalClasses?.tabsElement || '';
83
- const yearDropdownClass = generalClasses?.yearsDropdown || '';
84
-
85
- return (
86
- <div className={styles.page}>
87
- {features.length > 1 && <Tabs
88
- selectedTab={tab}
89
- onChangeTab={setTab}
90
- features={features}
91
- className={tabsClass}
92
- />}
93
- <div>
94
- {tab !== Feature.recentObservations && (
95
- <div style={{ float: "right" }} className={yearDropdownClass}>
96
- <Years
97
- value={year}
98
- numYears={featuresObj[tab]?.numYears || C.DEFAULT_NUM_YEARS}
99
- onChange={setYear}
100
- />
101
- </div>
102
- )}
103
- <h1 className={generalClasses?.pageHeadings}>{titles[tab]}</h1>
104
- </div>
105
- {getCurrentTab()}
106
- </div>
107
- );
108
- }
109
-
110
- export default TaxonPanel;
@@ -1,14 +0,0 @@
1
- import {render} from '@testing-library/react';
2
- import YearDropdown from '../year-dropdown';
3
-
4
- const defaultProps = {
5
- value: "1",
6
- onChange: () => null
7
- };
8
-
9
- describe('YearDropdown', () => {
10
- it('should render successfully', () => {
11
- const {baseElement} = render(<YearDropdown {...defaultProps} />);
12
- expect(baseElement).toBeTruthy();
13
- });
14
- });
@@ -1,3 +0,0 @@
1
- .dropdown {
2
- font-size: 18px;
3
- }
@@ -1,37 +0,0 @@
1
- import React, { useEffect, useState } from "react";
2
- import { getCurrentYear } from "../../__shared";
3
- import styles from "./year-dropdown.module.scss";
4
-
5
- interface YearsProps {
6
- value: string;
7
- onChange: (year: string) => void;
8
- numYears: number;
9
- className?: string;
10
- }
11
-
12
- const Years = ({ value, onChange, numYears, className }: YearsProps) => {
13
- const [years, setYears] = useState([]);
14
-
15
- useEffect(() => {
16
- const currentYear = getCurrentYear();
17
- const years: any = [];
18
- for (let i = currentYear; i>currentYear-numYears; i--) {
19
- years.push(i);
20
- }
21
- setYears(years);
22
- }, [numYears]);
23
-
24
- let classes = styles.dropdown;
25
- if (className) {
26
- classes += ` ${className}`;
27
- }
28
-
29
- return (
30
- <select className={classes} onChange={(e) => onChange(e.target.value)} defaultValue={value}>
31
- <option value="all">All years</option>
32
- {years.map((year) => <option value={year} key={year}>{year}</option>)}
33
- </select>
34
- )
35
- }
36
-
37
- export default Years;
@@ -1,81 +0,0 @@
1
- import { useEffect, useState } from "react";
2
- import { DataSource, DataSourceEnum } from "../__shared";
3
-
4
- type UseLoadSourceData = {
5
- action: any;
6
- taxonId?: number;
7
- placeId?: number;
8
- numResults?: number;
9
- source?: DataSource;
10
- dataUrl?: string;
11
- year?: number | string,
12
- isSummaryData?: boolean;
13
- };
14
-
15
- /**
16
- * This hook handles loading the iNat data from whatever source has been specified (DataSource):
17
- * nothing needs to be done.
18
- * - "autoLoad": the user is developing locally and wants to just ping iNat directly for the source
19
- * - "url": the user has generated the data sources at a URL somewhere via @imerss/inat-component-utils
20
- */
21
- export const useLoadSourceData = ({
22
- taxonId,
23
- placeId,
24
- numResults,
25
- source,
26
- dataUrl,
27
- year,
28
- action,
29
- isSummaryData = false
30
- }: UseLoadSourceData) => {
31
- const [loading, setLoading] = useState(false);
32
- const [results, setResults] = useState<any>([]);
33
-
34
- useEffect(() => {
35
- if (source === DataSourceEnum.autoLoad) {
36
- if (!taxonId) {
37
- console.error("Please supply a `taxonId` prop for the `autoLoad` source prop option.");
38
- return;
39
- }
40
- if (!placeId) {
41
- console.error("Please supply a `placeId` prop for the `autoLoad` source prop option.");
42
- return;
43
- }
44
-
45
- (async () => {
46
- setLoading(true);
47
-
48
- // note that `year` isn't used for recentObservations, but we pass it anyway to get more reusability out
49
- // of this hook
50
- const obs = await action({ taxonId, placeId, numResults, year });
51
- setResults(obs.results);
52
- setLoading(false);
53
- })();
54
- }
55
-
56
- if (source === DataSourceEnum.url) {
57
- if (!dataUrl) {
58
- console.error("Please supply a `dataUrl` prop for the `url` source prop option.");
59
- return;
60
- }
61
-
62
- (async () => {
63
- setLoading(true);
64
- const obs = await fetch(dataUrl);
65
- const json = await obs.json();
66
-
67
- // hack workaround. Better solution would have been to standardize the JSON structure at the top-level
68
- if (isSummaryData) {
69
- setResults(json);
70
- } else {
71
- setResults(json.results);
72
- }
73
- setLoading(false);
74
- })();
75
- }
76
- }, [source, dataUrl, taxonId, placeId, year, numResults, isSummaryData]);
77
-
78
- return {
79
- loading, results
80
- };
81
- }
package/src/index.ts DELETED
@@ -1,15 +0,0 @@
1
- import TaxonPanel from "./components/taxon-panel/taxon-panel";
2
-
3
- // front-end components
4
- export * from "./components/observation/observation";
5
- export * from "./components/common-taxa/common-taxa";
6
- export * from "./components/recent-observations/recent-observations";
7
- export * from "./components/favourites/favourites";
8
- export * from "./components/taxon-panel/taxon-panel";
9
- export * from "./components/seasonality-graph/seasonality-graph";
10
- export * from "./components/summary/summary";
11
-
12
- // all typings
13
- export * from "./__shared";
14
-
15
- export default TaxonPanel;
@@ -1,17 +0,0 @@
1
- import { TaxonPanelProps } from "../components/taxon-panel/taxon-panel";
2
-
3
- export {};
4
- declare global {
5
- interface Window {
6
- inatComponents: {
7
- initTaxonPanel: (domId: string, taxonPanelProps: TaxonPanelProps) => void;
8
- }
9
- }
10
- }
11
-
12
- window.initStandalone = window.initStandalone || {};
13
-
14
- export enum DataSource {
15
- autoLoad = "autoLoad",
16
- url = "url"
17
- }
@@ -1,6 +0,0 @@
1
- interface InitStandaloneProps {
2
- taxonId: number;
3
- placeId: number;
4
- }
5
-
6
- export type InitStandalone = (id: string, props: InitStandaloneProps) => void;
package/tsconfig.json DELETED
@@ -1,6 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.base.json",
3
- "exclude": [
4
- "src/**/*.test.tsx"
5
- ]
6
- }